From 2ac77aeec216ed02611a8ac2247d547ead10921a Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 17 Dec 2020 10:57:39 +0100 Subject: [PATCH 1/4] techdocs-backend: Remove individual preparers and generators from app backend, to use factory methods When a new Backstage app is setup, the techdocs backend file contains all the preparers (dir, github, gitlab, etc.) and same for generators. While this providers added customizibility, it comes with a cost of complexity in setting up the app backend. New preparers' updates would need users to update their app's techdocs backend file. Scaffolder backend has already moved to this proposed pattern. --- packages/backend/src/plugins/techdocs.ts | 35 +++++++------------ .../src/stages/generate/generators.ts | 18 ++++++++-- .../src/stages/prepare/preparers.ts | 35 +++++++++++++++++-- .../src/stages/publish/publish.test.ts | 23 ++++++++---- .../src/stages/publish/publish.ts | 12 ++++--- .../src/service/standaloneServer.ts | 2 +- 6 files changed, 87 insertions(+), 38 deletions(-) diff --git a/packages/backend/src/plugins/techdocs.ts b/packages/backend/src/plugins/techdocs.ts index 4822de9c5c..afe92cc347 100644 --- a/packages/backend/src/plugins/techdocs.ts +++ b/packages/backend/src/plugins/techdocs.ts @@ -15,12 +15,8 @@ */ import { createRouter, - DirectoryPreparer, Preparers, Generators, - TechdocsGenerator, - CommonGitPreparer, - UrlPreparer, Publisher, } from '@backstage/plugin-techdocs-backend'; import { PluginEnvironment } from '../types'; @@ -33,30 +29,25 @@ export default async function createPlugin({ reader, }: PluginEnvironment) { // Preparers are responsible for fetching source files for documentation. - const preparers = new Preparers(); - - const directoryPreparer = new DirectoryPreparer(logger); - preparers.register('dir', directoryPreparer); - - const commonGitPreparer = new CommonGitPreparer(logger); - preparers.register('github', commonGitPreparer); - preparers.register('gitlab', commonGitPreparer); - preparers.register('azure/api', commonGitPreparer); - - const urlPreparer = new UrlPreparer(reader, logger); - preparers.register('url', urlPreparer); + const preparers = await Preparers.fromConfig(config, { + logger, + reader, + }); // Generators are used for generating documentation sites. - const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(logger, config); - generators.register('techdocs', techdocsGenerator); + const generators = await Generators.fromConfig(config, { + logger, + }); - // Publishers are used for + // Publisher is used for // 1. Publishing generated files to storage // 2. Fetching files from storage and passing them to TechDocs frontend. - const publisher = Publisher.fromConfig(config, logger, discovery); + const publisher = await Publisher.fromConfig(config, { + logger, + discovery, + }); - // Docker client used by the generators. + // Docker client (conditionally) used by the generators, based on techdocs.generators config. const dockerClient = new Docker(); return await createRouter({ diff --git a/packages/techdocs-common/src/stages/generate/generators.ts b/packages/techdocs-common/src/stages/generate/generators.ts index f28a169942..cbfa5e7553 100644 --- a/packages/techdocs-common/src/stages/generate/generators.ts +++ b/packages/techdocs-common/src/stages/generate/generators.ts @@ -14,18 +14,32 @@ * limitations under the License. */ +import { Logger } from 'winston'; +import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { TechdocsGenerator } from '.'; 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(); + static async fromConfig( + config: Config, + { logger }: { logger: Logger }, + ): Promise { + const generators = new Generators(); + + const techdocsGenerator = new TechdocsGenerator(logger, config); + generators.register('techdocs', techdocsGenerator); + + return generators; + } + register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase) { this.generatorMap.set(generatorKey, generator); } diff --git a/packages/techdocs-common/src/stages/prepare/preparers.ts b/packages/techdocs-common/src/stages/prepare/preparers.ts index 52a47957e8..4a2d60eb31 100644 --- a/packages/techdocs-common/src/stages/prepare/preparers.ts +++ b/packages/techdocs-common/src/stages/prepare/preparers.ts @@ -13,14 +13,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { PreparerBase, RemoteProtocol, PreparerBuilder } from './types'; +import { Logger } from 'winston'; +import { UrlReader } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { DirectoryPreparer, CommonGitPreparer, UrlPreparer } from '.'; +import { PreparerBase, RemoteProtocol, PreparerBuilder } from './types'; import { parseReferenceAnnotation } from '../../helpers'; +type factoryOptions = { + logger: Logger; + reader: UrlReader; +}; + export class Preparers implements PreparerBuilder { private preparerMap = new Map(); + static async fromConfig( + // @ts-ignore + // Config not used now, but will be used in urlPreparer when it starts using + // @backstage/integration to get the tokens for providers. + config: Config, + { logger, reader }: factoryOptions, + ): Promise { + const preparers = new Preparers(); + + const directoryPreparer = new DirectoryPreparer(logger); + preparers.register('dir', directoryPreparer); + + const commonGitPreparer = new CommonGitPreparer(logger); + preparers.register('github', commonGitPreparer); + preparers.register('gitlab', commonGitPreparer); + preparers.register('azure/api', commonGitPreparer); + + const urlPreparer = new UrlPreparer(reader, logger); + preparers.register('url', urlPreparer); + + return preparers; + } + register(protocol: RemoteProtocol, preparer: PreparerBase) { this.preparerMap.set(protocol, preparer); } diff --git a/packages/techdocs-common/src/stages/publish/publish.test.ts b/packages/techdocs-common/src/stages/publish/publish.test.ts index dbced6db29..c61b9aa3ec 100644 --- a/packages/techdocs-common/src/stages/publish/publish.test.ts +++ b/packages/techdocs-common/src/stages/publish/publish.test.ts @@ -23,24 +23,27 @@ import { LocalPublish } from './local'; import { GoogleGCSPublish } from './googleStorage'; const logger = getVoidLogger(); -const testDiscovery: jest.Mocked = { +const discovery: jest.Mocked = { getBaseUrl: jest.fn().mockResolvedValueOnce('http://localhost:7000'), getExternalBaseUrl: jest.fn(), }; describe('Publisher', () => { - it('should create local publisher by default', () => { + it('should create local publisher by default', async () => { const mockConfig = new ConfigReader({ techdocs: { requestUrl: 'http://localhost:7000', }, }); - const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery); + const publisher = await Publisher.fromConfig(mockConfig, { + logger, + discovery, + }); expect(publisher).toBeInstanceOf(LocalPublish); }); - it('should create local publisher from config', () => { + it('should create local publisher from config', async () => { const mockConfig = new ConfigReader({ techdocs: { requestUrl: 'http://localhost:7000', @@ -50,11 +53,14 @@ describe('Publisher', () => { }, }); - const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery); + const publisher = await Publisher.fromConfig(mockConfig, { + logger, + discovery, + }); expect(publisher).toBeInstanceOf(LocalPublish); }); - it('should create google gcs publisher from config', () => { + it('should create google gcs publisher from config', async () => { const mockConfig = new ConfigReader({ techdocs: { requestUrl: 'http://localhost:7000', @@ -69,7 +75,10 @@ describe('Publisher', () => { }, }); - const publisher = Publisher.fromConfig(mockConfig, logger, testDiscovery); + const publisher = await Publisher.fromConfig(mockConfig, { + logger, + discovery, + }); expect(publisher).toBeInstanceOf(GoogleGCSPublish); }); }); diff --git a/packages/techdocs-common/src/stages/publish/publish.ts b/packages/techdocs-common/src/stages/publish/publish.ts index 04a9d89996..95b5cf83e2 100644 --- a/packages/techdocs-common/src/stages/publish/publish.ts +++ b/packages/techdocs-common/src/stages/publish/publish.ts @@ -21,16 +21,20 @@ import { PublisherType, PublisherBase } from './types'; import { LocalPublish } from './local'; import { GoogleGCSPublish } from './googleStorage'; +type factoryOptions = { + logger: Logger; + discovery: PluginEndpointDiscovery; +}; + /** * Factory class to create a TechDocs publisher based on defined publisher type in app config. * Uses `techdocs.publisher.type`. */ export class Publisher { - static fromConfig( + static async fromConfig( config: Config, - logger: Logger, - discovery: PluginEndpointDiscovery, - ): PublisherBase { + { logger, discovery }: factoryOptions, + ): Promise { const publisherType = (config.getOptionalString( 'techdocs.publisher.type', ) ?? 'local') as PublisherType; diff --git a/plugins/techdocs-backend/src/service/standaloneServer.ts b/plugins/techdocs-backend/src/service/standaloneServer.ts index 3faf736ca0..65a286feef 100644 --- a/plugins/techdocs-backend/src/service/standaloneServer.ts +++ b/plugins/techdocs-backend/src/service/standaloneServer.ts @@ -59,7 +59,7 @@ export async function startStandaloneServer( const techdocsGenerator = new TechdocsGenerator(logger, config); generators.register('techdocs', techdocsGenerator); - const publisher = Publisher.fromConfig(config, logger, discovery); + const publisher = await Publisher.fromConfig(config, { logger, discovery }); const dockerClient = new Docker(); From a8573e53b9edc74306f8168de834b849d6962606 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 17 Dec 2020 11:17:25 +0100 Subject: [PATCH 2/4] create-app: Update techdocs-backend, add changesets --- .changeset/curly-emus-own.md | 6 ++++ .changeset/thirty-papayas-attack.md | 36 +++++++++++++++++++ .../packages/backend/src/plugins/techdocs.ts | 33 +++++++++-------- 3 files changed, 60 insertions(+), 15 deletions(-) create mode 100644 .changeset/curly-emus-own.md create mode 100644 .changeset/thirty-papayas-attack.md diff --git a/.changeset/curly-emus-own.md b/.changeset/curly-emus-own.md new file mode 100644 index 0000000000..05ad1d5701 --- /dev/null +++ b/.changeset/curly-emus-own.md @@ -0,0 +1,6 @@ +--- +'@backstage/create-app': patch +--- + +techdocs-backend: Simplified file, removing individual preparers and generators. +techdocs-backend: UrlReader is now available to use in preparers. diff --git a/.changeset/thirty-papayas-attack.md b/.changeset/thirty-papayas-attack.md new file mode 100644 index 0000000000..253ec67e6b --- /dev/null +++ b/.changeset/thirty-papayas-attack.md @@ -0,0 +1,36 @@ +--- +'@backstage/techdocs-common': minor +'@backstage/plugin-techdocs-backend': minor +--- + +In your Backstage app, `packages/backend/plugins/techdocs.ts` file has now been simplified, +to remove registering individual preparers and generators. + +Please update the file when upgrading the version of `@backstage/plugin-techdocs-backend` package. + +```typescript +const preparers = await Preparers.fromConfig(config, { + logger, + reader, +}); + +const generators = await Generators.fromConfig(config, { + logger, +}); + +const publisher = await Publisher.fromConfig(config, { + logger, + discovery, +}); +``` + +You should be able to remove unnecessary imports, and just do + +```typescript +import { + createRouter, + Preparers, + Generators, + Publisher, +} from '@backstage/plugin-techdocs-backend'; +``` diff --git a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts index 1bbb5ff24b..5c7ec50ae6 100644 --- a/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts +++ b/packages/create-app/templates/default-app/packages/backend/src/plugins/techdocs.ts @@ -1,10 +1,7 @@ import { createRouter, - DirectoryPreparer, Preparers, Generators, - TechdocsGenerator, - CommonGitPreparer, Publisher, } from '@backstage/plugin-techdocs-backend'; import { PluginEnvironment } from '../types'; @@ -14,22 +11,28 @@ export default async function createPlugin({ logger, config, discovery, + reader, }: PluginEnvironment) { - const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(logger, config); + // Preparers are responsible for fetching source files for documentation. + const preparers = await Preparers.fromConfig(config, { + logger, + reader, + }); - generators.register('techdocs', techdocsGenerator); + // Generators are used for generating documentation sites. + const generators = await Generators.fromConfig(config, { + logger, + }); - const preparers = new Preparers(); - const directoryPreparer = new DirectoryPreparer(logger); - const commonGitPreparer = new CommonGitPreparer(logger); - - preparers.register('dir', directoryPreparer); - preparers.register('github', commonGitPreparer); - preparers.register('gitlab', commonGitPreparer); - - const publisher = Publisher.fromConfig(config, logger, discovery); + // Publisher is used for + // 1. Publishing generated files to storage + // 2. Fetching files from storage and passing them to TechDocs frontend. + const publisher = await Publisher.fromConfig(config, { + logger, + discovery, + }); + // Docker client (conditionally) used by the generators, based on techdocs.generators config. const dockerClient = new Docker(); return await createRouter({ From 305693a59a11efc4cd699bb6eb5376bfa49d4aea Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Thu, 17 Dec 2020 11:42:08 +0100 Subject: [PATCH 3/4] docs: Update techdocs backend instructions to use latest factory methods Smaller than before <3 --- docs/features/techdocs/getting-started.md | 35 +++++++++-------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/docs/features/techdocs/getting-started.md b/docs/features/techdocs/getting-started.md index c82bc8b95b..e1ddfd23f9 100644 --- a/docs/features/techdocs/getting-started.md +++ b/docs/features/techdocs/getting-started.md @@ -72,12 +72,8 @@ add the following ```typescript import { createRouter, - DirectoryPreparer, Preparers, Generators, - TechdocsGenerator, - CommonGitPreparer, - UrlPreparer, Publisher, } from '@backstage/plugin-techdocs-backend'; import { PluginEnvironment } from '../types'; @@ -90,30 +86,25 @@ export default async function createPlugin({ reader, }: PluginEnvironment) { // Preparers are responsible for fetching source files for documentation. - const preparers = new Preparers(); - - const directoryPreparer = new DirectoryPreparer(logger); - preparers.register('dir', directoryPreparer); - - const commonGitPreparer = new CommonGitPreparer(logger); - preparers.register('github', commonGitPreparer); - preparers.register('gitlab', commonGitPreparer); - preparers.register('azure/api', commonGitPreparer); - - const urlPreparer = new UrlPreparer(reader, logger); - preparers.register('url', urlPreparer); + const preparers = await Preparers.fromConfig(config, { + logger, + reader, + }); // Generators are used for generating documentation sites. - const generators = new Generators(); - const techdocsGenerator = new TechdocsGenerator(logger, config); - generators.register('techdocs', techdocsGenerator); + const generators = await Generators.fromConfig(config, { + logger, + }); - // Publishers are used for + // Publisher is used for // 1. Publishing generated files to storage // 2. Fetching files from storage and passing them to TechDocs frontend. - const publisher = Publisher.fromConfig(config, logger, discovery); + const publisher = await Publisher.fromConfig(config, { + logger, + discovery, + }); - // Docker client used by the generators. + // Docker client (conditionally) used by the generators, based on techdocs.generators config. const dockerClient = new Docker(); return await createRouter({ From 4a62be4bcb78d2a151c0dac25936217acb616f8a Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Mon, 21 Dec 2020 12:57:52 +0100 Subject: [PATCH 4/4] changesets: Use techdocs migration guide in create-app changelog as well --- .changeset/curly-emus-own.md | 6 ------ .changeset/thirty-papayas-attack.md | 4 ++++ 2 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 .changeset/curly-emus-own.md diff --git a/.changeset/curly-emus-own.md b/.changeset/curly-emus-own.md deleted file mode 100644 index 05ad1d5701..0000000000 --- a/.changeset/curly-emus-own.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -techdocs-backend: Simplified file, removing individual preparers and generators. -techdocs-backend: UrlReader is now available to use in preparers. diff --git a/.changeset/thirty-papayas-attack.md b/.changeset/thirty-papayas-attack.md index 253ec67e6b..93a31bb6b5 100644 --- a/.changeset/thirty-papayas-attack.md +++ b/.changeset/thirty-papayas-attack.md @@ -1,8 +1,12 @@ --- +'@backstage/create-app': patch '@backstage/techdocs-common': minor '@backstage/plugin-techdocs-backend': minor --- +techdocs-backend: Simplified file, removing individual preparers and generators. +techdocs-backend: UrlReader is now available to use in preparers. + In your Backstage app, `packages/backend/plugins/techdocs.ts` file has now been simplified, to remove registering individual preparers and generators.