diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md index f189a85c0c..53d107bd17 100644 --- a/packages/techdocs-common/api-report.md +++ b/packages/techdocs-common/api-report.md @@ -17,21 +17,19 @@ import { ScmIntegrationRegistry } from '@backstage/integration'; import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; -// Warning: (ae-missing-release-tag) "DirectoryPreparer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) +// @public export class DirectoryPreparer implements PreparerBase { - constructor(config: Config, _logger: Logger_2, reader: UrlReader); + // @deprecated + constructor(config: Config, _logger: Logger_2 | null, reader: UrlReader); + // Warning: (ae-forgotten-export) The symbol "DirectoryPreparerOptions" needs to be exported by the entry point index.d.ts + // + // (undocumented) + static fromConfig(options: DirectoryPreparerOptions): DirectoryPreparer; + // Warning: (ae-forgotten-export) The symbol "PreparerOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-forgotten-export) The symbol "PreparerResponse" needs to be exported by the entry point index.d.ts // // (undocumented) - prepare( - entity: Entity, - options?: { - logger?: Logger_2; - etag?: string; - }, - ): Promise; + prepare(entity: Entity, options?: PreparerOptions): Promise; } // Warning: (ae-missing-release-tag) "GeneratorBase" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/packages/techdocs-common/config.d.ts b/packages/techdocs-common/config.d.ts new file mode 100644 index 0000000000..54e76c1a1c --- /dev/null +++ b/packages/techdocs-common/config.d.ts @@ -0,0 +1,195 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export interface Config { + app: { + baseUrl: string; // defined in core, but repeated here without doc + }; + + backend: { + /** Backend configuration for when request authentication is enabled */ + auth?: { + /** Keys shared by all backends for signing and validating backend tokens. */ + keys: { + /** + * Secret for generating tokens. Should be a base64 string, recommended + * length is 24 bytes. + * + * @visibility secret + */ + secret: string; + }[]; + }; + + baseUrl: string; // defined in core, but repeated here without doc + + /** Address that the backend should listen to. */ + listen: + | string + | { + /** Address of the interface that the backend should bind to. */ + host?: string; + /** Port that the backend should listen to. */ + port?: string | number; + }; + + /** + * HTTPS configuration for the backend. If omitted the backend will serve HTTP. + * + * Setting this to `true` will cause self-signed certificates to be generated, which + * can be useful for local development or other non-production scenarios. + */ + https?: + | true + | { + /** Certificate configuration */ + certificate?: { + /** PEM encoded certificate. Use $file to load in a file */ + cert: string; + /** + * PEM encoded certificate key. Use $file to load in a file. + * @visibility secret + */ + key: string; + }; + }; + + /** Database connection configuration, select base database type using the `client` field */ + database: { + /** Default database client to use */ + client: 'sqlite3' | 'pg'; + /** + * Base database connection string or Knex object + * @secret + */ + connection: string | object; + /** Database name prefix override */ + prefix?: string; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to true if unspecified. + */ + ensureExists?: boolean; + /** + * How plugins databases are managed/divided in the provided database instance. + * + * `database` -> Plugins are each given their own database to manage their schemas/tables. + * + * `schema` -> Plugins will be given their own schema (in the specified/default database) + * to manage their tables. + * + * NOTE: Currently only supported by the `pg` client. + * + * @default database + */ + pluginDivisionMode?: 'database' | 'schema'; + /** + * Arbitrary config object to pass to knex when initializing + * (https://knexjs.org/#Installation-client). Most notable is the debug + * and asyncStackTraces booleans + */ + knexConfig?: object; + /** Plugin specific database configuration and client override */ + plugin?: { + [pluginId: string]: { + /** Database client override */ + client?: 'sqlite3' | 'pg'; + /** + * Database connection string or Knex object override + * @secret + */ + connection?: string | object; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to base config if unspecified. + */ + ensureExists?: boolean; + /** + * Arbitrary config object to pass to knex when initializing + * (https://knexjs.org/#Installation-client). Most notable is the + * debug and asyncStackTraces booleans. + * + * This is merged recursively into the base knexConfig + */ + knexConfig?: object; + }; + }; + }; + + /** Cache connection configuration, select cache type using the `store` field */ + cache?: + | { + store: 'memory'; + } + | { + store: 'memcache'; + /** + * A memcache connection string in the form `user:pass@host:port`. + * @secret + */ + connection: string; + }; + + cors?: { + origin?: string | string[]; + methods?: string | string[]; + allowedHeaders?: string | string[]; + exposedHeaders?: string | string[]; + credentials?: boolean; + maxAge?: number; + preflightContinue?: boolean; + optionsSuccessStatus?: number; + }; + + /** + * Configuration related to URL reading, used for example for reading catalog info + * files, scaffolder templates, and techdocs content. + */ + reading?: { + /** + * A list of targets to allow outgoing requests to. Users will be able to make + * requests on behalf of the backend to the targets that are allowed by this list. + */ + allow?: Array<{ + /** + * A host to allow outgoing requests to, being either a full host or + * a subdomain wildcard pattern with a leading `*`. For example `example.com` + * and `*.example.com` are valid values, `prod.*.example.com` is not. + * The host may also contain a port, for example `example.com:8080`. + */ + host: string; + + /** + * An optional list of paths. In case they are present only targets matching + * any of them will are allowed. You can use trailing slashes to make sure only + * subdirectories are allowed, for example `/mydir/` will allow targets with + * paths like `/mydir/a` but will block paths like `/mydir2`. + */ + paths?: string[]; + }>; + }; + + /** + * Content Security Policy options. + * + * The keys are the plain policy ID, e.g. "upgrade-insecure-requests". The + * values are on the format that the helmet library expects them, as an + * array of strings. There is also the special value false, which means to + * remove the default value that Backstage puts in place for that policy. + */ + csp?: { [policyId: string]: string[] | false }; + }; +} diff --git a/packages/techdocs-common/src/stages/generate/generators.ts b/packages/techdocs-common/src/stages/generate/generators.ts index de39d4e7c3..22d07a49b9 100644 --- a/packages/techdocs-common/src/stages/generate/generators.ts +++ b/packages/techdocs-common/src/stages/generate/generators.ts @@ -26,9 +26,18 @@ import { SupportedGeneratorKey, } from './types'; +/** + * Collection of docs generators + * @public + */ export class Generators implements GeneratorBuilder { private generatorMap = new Map(); + /** + * Returns a generators instance containing a generator for Tech Docs + * @param config - A Backstage configuration + * @param options - Options to configure the Tech Docs generator + */ static async fromConfig( config: Config, options: { logger: Logger; containerRunner: ContainerRunner }, @@ -41,10 +50,19 @@ export class Generators implements GeneratorBuilder { return generators; } + /** + * Register a generator in the generators collection + * @param generatorKey - Unique identifier for the generator + * @param generator - The generator instance to register + */ register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase) { this.generatorMap.set(generatorKey, generator); } + /** + * Returns the generator for a given Tech Docs entity + * @param entity - A Tech Docs entity instance + */ get(entity: Entity): GeneratorBase { const generatorKey = getGeneratorKey(entity); const generator = this.generatorMap.get(generatorKey); diff --git a/packages/techdocs-common/src/stages/generate/index.ts b/packages/techdocs-common/src/stages/generate/index.ts index eb015a5c2d..5b442be1ae 100644 --- a/packages/techdocs-common/src/stages/generate/index.ts +++ b/packages/techdocs-common/src/stages/generate/index.ts @@ -16,7 +16,9 @@ export { TechdocsGenerator } from './techdocs'; export { Generators } from './generators'; export type { - GeneratorBuilder, GeneratorBase, + GeneratorBuilder, + GeneratorFactory, GeneratorRunOptions, + SupportedGeneratorKey, } from './types'; diff --git a/packages/techdocs-common/src/stages/generate/techdocs.ts b/packages/techdocs-common/src/stages/generate/techdocs.ts index 99554a6218..c6a170910d 100644 --- a/packages/techdocs-common/src/stages/generate/techdocs.ts +++ b/packages/techdocs-common/src/stages/generate/techdocs.ts @@ -38,7 +38,12 @@ import { GeneratorRunOptions, } from './types'; import { ForwardedError } from '@backstage/errors'; +import { GeneratorFactory } from './types'; +/** + * Generates documentation files + * @public + */ export class TechdocsGenerator implements GeneratorBase { /** * The default docker image (and version) used to generate content. Public @@ -50,10 +55,12 @@ export class TechdocsGenerator implements GeneratorBase { private readonly options: GeneratorConfig; private readonly scmIntegrations: ScmIntegrationRegistry; - static fromConfig( - config: Config, - options: { containerRunner: ContainerRunner; logger: Logger }, - ) { + /** + * Returns a instance of Tech Docs generator + * @param config - A Backstage configuration + * @param options - Options to configure the generator + */ + static fromConfig(config: Config, options: GeneratorFactory) { const { containerRunner, logger } = options; const scmIntegrations = ScmIntegrations.fromConfig(config); return new TechdocsGenerator({ @@ -76,6 +83,7 @@ export class TechdocsGenerator implements GeneratorBase { this.scmIntegrations = options.scmIntegrations; } + /** {@inheritDoc GeneratorBase.run} */ public async run(options: GeneratorRunOptions): Promise { const { inputDir, diff --git a/packages/techdocs-common/src/stages/generate/types.ts b/packages/techdocs-common/src/stages/generate/types.ts index 2a090d5584..4903d886f1 100644 --- a/packages/techdocs-common/src/stages/generate/types.ts +++ b/packages/techdocs-common/src/stages/generate/types.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ContainerRunner } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import { Writable } from 'stream'; import { Logger } from 'winston'; @@ -22,6 +23,15 @@ import { ParsedLocationAnnotation } from '../../helpers'; // Determines where the generator will be run export type GeneratorRunInType = 'docker' | 'local'; +/** + * Options for building generators + * @public + */ +export type GeneratorFactory = { + containerRunner: ContainerRunner; + logger: Logger; +}; + /** * The techdocs generator configurations options. */ @@ -51,18 +61,27 @@ export type GeneratorRunOptions = { logStream?: Writable; }; +/** + * Generates documentation files + * @public + */ export type GeneratorBase = { - // Runs the generator with the values + /** + * Runs the generator with the values + * @public + */ run(opts: GeneratorRunOptions): Promise; }; /** * List of supported generator options + * @public */ export type SupportedGeneratorKey = 'techdocs' | string; /** * The generator builder holds the generator ready for run time + * @public */ export type GeneratorBuilder = { register(protocol: SupportedGeneratorKey, generator: GeneratorBase): void; diff --git a/packages/techdocs-common/src/stages/prepare/dir.ts b/packages/techdocs-common/src/stages/prepare/dir.ts index ade0b00162..c8b8943266 100644 --- a/packages/techdocs-common/src/stages/prepare/dir.ts +++ b/packages/techdocs-common/src/stages/prepare/dir.ts @@ -26,18 +26,43 @@ import { Logger } from 'winston'; import { parseReferenceAnnotation, transformDirLocation } from '../../helpers'; import { PreparerBase, PreparerResponse } from './types'; +export type DirectoryPreparerOptions = { + config: Config; + reader: UrlReader; +}; + +export type PreparerOptions = { logger?: Logger; etag?: string }; + +/** + * Prepares files before building documentation + * + * @public + */ export class DirectoryPreparer implements PreparerBase { private readonly scmIntegrations: ScmIntegrationRegistry; private readonly reader: UrlReader; - constructor(config: Config, _logger: Logger, reader: UrlReader) { + /** + * @deprecated use static fromConfig method instead. + */ + constructor(config: Config, _logger: Logger | null, reader: UrlReader) { this.reader = reader; this.scmIntegrations = ScmIntegrations.fromConfig(config); } + static fromConfig(options: DirectoryPreparerOptions): DirectoryPreparer { + return new DirectoryPreparer(options.config, null, options.reader); + } + + /** + * + * @param entity - The parts of the format that's common to all versions/kinds of entity + * @param options - Optional logger and etag + * @returns + */ async prepare( entity: Entity, - options?: { logger?: Logger; etag?: string }, + options?: PreparerOptions, ): Promise { const annotation = parseReferenceAnnotation( 'backstage.io/techdocs-ref',