diff --git a/.changeset/sweet-feet-study.md b/.changeset/sweet-feet-study.md new file mode 100644 index 0000000000..4afdb467f6 --- /dev/null +++ b/.changeset/sweet-feet-study.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-linguist-backend': minor +--- + +**BREAKING**: Removed the alpha export plugin options from exported `linguistPlugin()` (used by the new backend system) in favour of static config. diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index bbb7290c73..f3b1ea06f8 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -36,7 +36,6 @@ import { badgesPlugin } from '@backstage/plugin-badges-backend'; import { azureDevOpsPlugin } from '@backstage/plugin-azure-devops-backend'; import { linguistPlugin } from '@backstage/plugin-linguist-backend'; import { devtoolsPlugin } from '@backstage/plugin-devtools-backend'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; import { adrPlugin } from '@backstage/plugin-adr-backend'; import { lighthousePlugin } from '@backstage/plugin-lighthouse-backend'; import { proxyPlugin } from '@backstage/plugin-proxy-backend'; @@ -58,20 +57,7 @@ backend.add(devtoolsPlugin()); backend.add(entityFeedbackPlugin()); // Linguist -const linguistSchedule: TaskScheduleDefinition = { - frequency: { minutes: 2 }, - timeout: { minutes: 15 }, - initialDelay: { seconds: 15 }, -}; - -backend.add( - linguistPlugin({ - schedule: linguistSchedule, - age: { days: 30 }, - batchSize: 2, - useSourceLocation: false, - }), -); +backend.add(linguistPlugin()); // Todo backend.add(todoPlugin()); diff --git a/plugins/linguist-backend/README.md b/plugins/linguist-backend/README.md index 7bca98191b..d2ed7f5f03 100644 --- a/plugins/linguist-backend/README.md +++ b/plugins/linguist-backend/README.md @@ -70,24 +70,32 @@ In your `packages/backend/src/index.ts` make the following changes: // ... other feature additions -+ const linguistSchedule: TaskScheduleDefinition = { -+ frequency: { minutes: 2 }, -+ timeout: { minutes: 15 }, -+ initialDelay: { seconds: 15 }, -+ }; - -+ backend.add( -+ linguistPlugin({ -+ schedule: linguistSchedule, -+ age: { days: 30 }, -+ batchSize: 2, -+ useSourceLocation: false, -+ }), -+ ); ++ backend.add(linguistPlugin()); backend.start(); ``` +The plugin options can be set through the `app-config.yaml`: + +```yaml +// ... + +linguist: + schedule: + frequency: + minutes: 2 + timeout: + minutes: 2 + initialDelay: + seconds: 15 + age: + days: 30 + batchSize: 2 + useSourceLocation: false + +// ... +``` + ## Plugin Option The Linguist backend has various plugin options that you can provide to the `createRouter` function in your `packages/backend/src/plugins/linguist.ts` file that will allow you to configure various aspects of how it works. The following sections go into the details of these options diff --git a/plugins/linguist-backend/api-report.md b/plugins/linguist-backend/api-report.md index aac978e426..4edd939058 100644 --- a/plugins/linguist-backend/api-report.md +++ b/plugins/linguist-backend/api-report.md @@ -27,6 +27,11 @@ export function createRouter( routerOptions: RouterOptions, ): Promise; +// @public (undocumented) +export function createRouterFromConfig( + routerOptions: RouterOptions, +): Promise; + // @public (undocumented) export interface LinguistBackendApi { // (undocumented) @@ -36,23 +41,7 @@ export interface LinguistBackendApi { } // @public -export const linguistPlugin: (options: LinguistPluginOptions) => BackendFeature; - -// @public -export interface LinguistPluginOptions { - // (undocumented) - age?: HumanDuration; - // (undocumented) - batchSize?: number; - // (undocumented) - kind?: string[]; - // (undocumented) - linguistJsOptions?: Record; - // (undocumented) - schedule?: TaskScheduleDefinition; - // (undocumented) - useSourceLocation?: boolean; -} +export const linguistPlugin: () => BackendFeature; // @public export class LinguistTagsProcessor implements CatalogProcessor { @@ -104,6 +93,8 @@ export interface PluginOptions { // @public (undocumented) export interface RouterOptions { + // (undocumented) + config?: Config; // (undocumented) database: PluginDatabaseManager; // (undocumented) diff --git a/plugins/linguist-backend/config.d.ts b/plugins/linguist-backend/config.d.ts index e7ad487d12..758210feb7 100644 --- a/plugins/linguist-backend/config.d.ts +++ b/plugins/linguist-backend/config.d.ts @@ -14,11 +14,35 @@ * limitations under the License. */ +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; import { HumanDuration } from '@backstage/types'; +import { Options as LinguistJsOptions } from 'linguist-js/dist/types'; export interface Config { /** Configuration options for the linguist plugin */ linguist?: { + schedule?: TaskScheduleDefinition; + /** + * @default 20 + */ + batchSize?: number; + /** + * @default false + */ + useSourceLocation?: boolean; + /** + * Refresh generated language breakdown + */ + age?: HumanDuration; + /** + * @default ['API', 'Component', 'Template'] + */ + kind?: string[]; + /** + * [linguist-js](https://www.npmjs.com/package/linguist-js) options + */ + linguistJsOptions?: LinguistJsOptions; + /** Options for the tags processor */ tagsProcessor?: { /** diff --git a/plugins/linguist-backend/src/index.ts b/plugins/linguist-backend/src/index.ts index 107877551e..64016d0d07 100644 --- a/plugins/linguist-backend/src/index.ts +++ b/plugins/linguist-backend/src/index.ts @@ -24,4 +24,3 @@ export * from './processor'; export * from './service/router'; export type { LinguistBackendApi } from './api'; export { linguistPlugin } from './plugin'; -export type { LinguistPluginOptions } from './plugin'; diff --git a/plugins/linguist-backend/src/plugin.ts b/plugins/linguist-backend/src/plugin.ts index f655d6a1f4..cbf18d9ebe 100644 --- a/plugins/linguist-backend/src/plugin.ts +++ b/plugins/linguist-backend/src/plugin.ts @@ -19,69 +19,50 @@ import { coreServices, createBackendPlugin, } from '@backstage/backend-plugin-api'; -import { createRouter } from './service/router'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; -import { HumanDuration } from '@backstage/types'; -/** - * Options for Linguist backend plugin - * - * @public - */ -export interface LinguistPluginOptions { - schedule?: TaskScheduleDefinition; - age?: HumanDuration; - batchSize?: number; - useSourceLocation?: boolean; - linguistJsOptions?: Record; - kind?: string[]; -} +import { createRouterFromConfig } from './service/router'; /** * Linguist backend plugin * * @public */ -export const linguistPlugin = createBackendPlugin( - (options: LinguistPluginOptions) => ({ - pluginId: 'linguist', - register(env) { - env.registerInit({ - deps: { - logger: coreServices.logger, - reader: coreServices.urlReader, - database: coreServices.database, - discovery: coreServices.discovery, - scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, - httpRouter: coreServices.httpRouter, - }, - async init({ - logger, - reader, - database, - discovery, - scheduler, - tokenManager, - httpRouter, - }) { - httpRouter.use( - await createRouter( - { - ...options, - }, - { - logger: loggerToWinstonLogger(logger), - reader, - database, - discovery, - scheduler, - tokenManager, - }, - ), - ); - }, - }); - }, - }), -); +export const linguistPlugin = createBackendPlugin({ + pluginId: 'linguist', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + reader: coreServices.urlReader, + database: coreServices.database, + discovery: coreServices.discovery, + scheduler: coreServices.scheduler, + tokenManager: coreServices.tokenManager, + httpRouter: coreServices.httpRouter, + }, + async init({ + logger, + config, + reader, + database, + discovery, + scheduler, + tokenManager, + httpRouter, + }) { + httpRouter.use( + await createRouterFromConfig({ + logger: loggerToWinstonLogger(logger), + config, + reader, + database, + discovery, + scheduler, + tokenManager, + }), + ); + }, + }); + }, +}); diff --git a/plugins/linguist-backend/src/service/router.test.ts b/plugins/linguist-backend/src/service/router.test.ts index 7ab8f1c238..4048e1213b 100644 --- a/plugins/linguist-backend/src/service/router.test.ts +++ b/plugins/linguist-backend/src/service/router.test.ts @@ -25,7 +25,7 @@ import { import express from 'express'; import request from 'supertest'; import { ConfigReader } from '@backstage/config'; -import { createRouter } from './router'; +import { createRouter, createRouterFromConfig } from './router'; import { LinguistBackendApi } from '../api'; import { TaskScheduleDefinition } from '@backstage/backend-tasks'; @@ -69,26 +69,57 @@ describe('createRouter', () => { let linguistBackendApi: jest.Mocked; let app: express.Express; - beforeAll(async () => { - const router = await createRouter( - { schedule: schedule, age: { days: 30 }, useSourceLocation: false }, - { - linguistBackendApi: linguistBackendApi, - discovery: testDiscovery, - database: createDatabase(), - reader: mockUrlReader, - logger: getVoidLogger(), - tokenManager: mockedTokenManager, - }, - ); - app = express().use(router); - }); - beforeEach(() => { jest.resetAllMocks(); }); describe('GET /health', () => { + beforeAll(async () => { + const router = await createRouter( + { schedule: schedule, age: { days: 30 }, useSourceLocation: false }, + { + linguistBackendApi: linguistBackendApi, + discovery: testDiscovery, + database: createDatabase(), + reader: mockUrlReader, + logger: getVoidLogger(), + tokenManager: mockedTokenManager, + }, + ); + app = express().use(router); + }); + it('returns ok', async () => { + const response = await request(app).get('/health'); + + expect(response.status).toEqual(200); + expect(response.body).toEqual({ status: 'ok' }); + }); + }); + + describe('GET /health from config', () => { + beforeAll(async () => { + const config = new ConfigReader({ + linguist: { + schedule: { + frequency: { minutes: 2 }, + timeout: { minutes: 15 }, + initialDelay: { seconds: 15 }, + }, + age: { days: 30 }, + useSourceLocation: false, + }, + }); + const router = await createRouterFromConfig({ + linguistBackendApi: linguistBackendApi, + discovery: testDiscovery, + database: createDatabase(), + reader: mockUrlReader, + logger: getVoidLogger(), + config, + tokenManager: mockedTokenManager, + }); + app = express().use(router); + }); it('returns ok', async () => { const response = await request(app).get('/health'); diff --git a/plugins/linguist-backend/src/service/router.ts b/plugins/linguist-backend/src/service/router.ts index 9e45438ec8..02ff05f398 100644 --- a/plugins/linguist-backend/src/service/router.ts +++ b/plugins/linguist-backend/src/service/router.ts @@ -28,11 +28,13 @@ import { LinguistBackendApi } from '../api'; import { LinguistBackendDatabase } from '../db'; import { PluginTaskScheduler, + readTaskScheduleDefinitionFromConfig, TaskScheduleDefinition, } from '@backstage/backend-tasks'; import { HumanDuration } from '@backstage/types'; import { CatalogClient } from '@backstage/catalog-client'; import { LinguistBackendClient } from '../api/LinguistBackendClient'; +import { Config } from '@backstage/config'; /** @public */ export interface PluginOptions { @@ -53,6 +55,7 @@ export interface RouterOptions { database: PluginDatabaseManager; discovery: PluginEndpointDiscovery; scheduler?: PluginTaskScheduler; + config?: Config; } /** @public */ @@ -60,6 +63,9 @@ export async function createRouter( pluginOptions: PluginOptions, routerOptions: RouterOptions, ): Promise { + const { logger, reader, database, discovery, scheduler, tokenManager } = + routerOptions; + const { schedule, age, @@ -69,9 +75,6 @@ export async function createRouter( linguistJsOptions, } = pluginOptions; - const { logger, reader, database, discovery, scheduler, tokenManager } = - routerOptions; - const linguistBackendStore = await LinguistBackendDatabase.create( await database.getClient(), ); @@ -135,3 +138,28 @@ export async function createRouter( router.use(errorHandler()); return router; } + +/** @public */ +export async function createRouterFromConfig(routerOptions: RouterOptions) { + const { config } = routerOptions; + const pluginOptions: PluginOptions = {}; + if (config) { + if (config.has('linguist.schedule')) { + pluginOptions.schedule = readTaskScheduleDefinitionFromConfig( + config.getConfig('linguist.schedule'), + ); + } + pluginOptions.batchSize = config.getOptionalNumber('linguist.batchSize'); + pluginOptions.useSourceLocation = config.getBoolean( + 'linguist.useSourceLocation', + ); + pluginOptions.age = config.getOptionalConfig('linguist.age') as + | HumanDuration + | undefined; + pluginOptions.kind = config.getOptionalStringArray('linguist.kind'); + pluginOptions.linguistJsOptions = config.getOptionalConfig( + 'linguist.linguistJsOptions', + ); + } + return createRouter(pluginOptions, routerOptions); +}