From 7b3ed9b928b5997aa5bb0a5e6976ee7a2a85280a Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sun, 27 Aug 2023 18:36:03 -0400 Subject: [PATCH 01/16] Add Bitbucket Server Event Integration Signed-off-by: David Lilienfeld --- .changeset/nine-falcons-repeat.md | 6 + .../integrations/bitbucketServer/discovery.md | 96 +++- .../package.json | 8 + .../report.api.md | 75 ++- .../src/index.ts | 9 +- .../src/lib/BitbucketServerClient.test.ts | 87 ++- .../src/lib/BitbucketServerClient.ts | 44 +- .../src/lib/index.ts | 5 +- .../src/lib/types.ts | 51 +- ...oduleBitbucketServerEntityProvider.test.ts | 32 +- ...alogModuleBitbucketServerEntityProvider.ts | 24 +- .../BitbucketServerEntityProvider.test.ts | 495 +++++++++++++++++- .../BitbucketServerEntityProvider.ts | 413 ++++++++++++++- .../BitbucketServerEntityProviderConfig.ts | 2 - .../BitbucketServerLocationParser.ts | 8 +- .../.eslintrc.js | 1 + .../README.md | 48 ++ .../api-report-alpha.md | 14 + .../api-report.md | 18 + .../catalog-info.yaml | 9 + .../package.json | 55 ++ .../src/alpha.ts | 18 + .../src/index.ts | 24 + .../router/BitbucketServerEventRouter.test.ts | 53 ++ .../src/router/BitbucketServerEventRouter.ts | 45 ++ .../eventsModuleBitbucketServerEventRouter.ts | 44 ++ .../eventsModuleBitbucketServerRouter.test.ts | 44 ++ .../src/setupTests.ts | 17 + yarn.lock | 15 + 29 files changed, 1715 insertions(+), 45 deletions(-) create mode 100644 .changeset/nine-falcons-repeat.md create mode 100644 plugins/events-backend-module-bitbucket-server/.eslintrc.js create mode 100644 plugins/events-backend-module-bitbucket-server/README.md create mode 100644 plugins/events-backend-module-bitbucket-server/api-report-alpha.md create mode 100644 plugins/events-backend-module-bitbucket-server/api-report.md create mode 100644 plugins/events-backend-module-bitbucket-server/catalog-info.yaml create mode 100644 plugins/events-backend-module-bitbucket-server/package.json create mode 100644 plugins/events-backend-module-bitbucket-server/src/alpha.ts create mode 100644 plugins/events-backend-module-bitbucket-server/src/index.ts create mode 100644 plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.test.ts create mode 100644 plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.ts create mode 100644 plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts create mode 100644 plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts create mode 100644 plugins/events-backend-module-bitbucket-server/src/setupTests.ts diff --git a/.changeset/nine-falcons-repeat.md b/.changeset/nine-falcons-repeat.md new file mode 100644 index 0000000000..a67e925343 --- /dev/null +++ b/.changeset/nine-falcons-repeat.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend-module-bitbucket-server': minor +'@backstage/plugin-events-backend-module-bitbucket-server': minor +--- + +Added the ability for the plugin to receive events coming from Bitbucket Server push webhooks. It then performs a delta mutation on the catalog. diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md index 9ba6c7a96d..a66db83806 100644 --- a/docs/integrations/bitbucketServer/discovery.md +++ b/docs/integrations/bitbucketServer/discovery.md @@ -26,7 +26,47 @@ dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to you yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server ``` -And update your backend by adding the following line: +### Installation with New Backend System + +```ts +// optional if you want HTTP endpojnts to receive external events +// backend.add(import('@backstage/plugin-events-backend/alpha')); +// optional if you want to use AWS SQS instead of HTTP endpoints to receive external events +// backend.add(import('@backstage/plugin-events-backend-module-aws-sqs/alpha')); +backend.add( + import('@backstage/plugin-events-backend-module-bitbucket-server/alpha'), +); +backend.add( + import('@backstage/plugin-catalog-backend-module-bitbucket-server/alpha'), +); +``` + +You need to decide how you want to receive events from external sources like + +- [via HTTP endpoint](https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md) +- [via an AWS SQS queue](https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-aws-sqs/README.md) + +Further documentation: + +- +- +- + +### Installation with Legacy Backend System + +#### Installation without Events Support + +You will have to add the entity provider in the catalog initialization code of your +backend. The provider is not installed by default, therefore you have to add a +dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend +package. + +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server +``` + +And then add the entity provider to your catalog builder: ```ts title="packages/backend/src/index.ts" backend.add(import('@backstage/plugin-catalog-backend')); @@ -37,6 +77,60 @@ backend.add( /* highlight-add-end */ ``` +#### Installation with Events Support + +Please follow the installation instructions at + +- +- + +Additionally, you need to decide how you want to receive events from external sources like + +- [via HTTP endpoint](https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md) + - Bitbucket Server events webhook url should be set to `{backstageBaseUrl}/api/events/http/bitbucketServer` +- [via an AWS SQS queue](https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-aws-sqs/README.md) + +Set up your provider + +```ts title="packages/backend/src/plugins/catalog.ts" +import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; +/* highlight-add-start */ +import { BitbucketServerEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-server'; +/* highlight-add-end */ + +import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend'; +import { Router } from 'express'; +import { PluginEnvironment } from '../types'; + +export default async function createPlugin( + env: PluginEnvironment, +): Promise { + const builder = await CatalogBuilder.create(env); + builder.addProcessor(new ScaffolderEntitiesProcessor()); + /* highlight-add-start */ + const bitbucketServerProvider = BitbucketServerEntityProvider.fromConfig( + env.config, + { + catalogApi: new CatalogClient({ discoveryApi: env.discovery }), + logger: env.logger, + scheduler: env.scheduler, + events: env.events, + tokenManager: env.tokenManager, + }, + ); + env.eventBroker.subscribe(bitbucketServerProvider); + builder.addEntityProvider(bitbucketServerProvider); + /* highlight-add-end */ + const { processingEngine, router } = await builder.build(); + await processingEngine.start(); + return router; +} +``` + +**Attention:** +`catalogApi` and `tokenManager` are required at this variant +compared to the one without events support. + ## Configuration To use the entity provider, you'll need a [Bitbucket Server integration set up](locations.md). diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 6113f4b41b..a7c403b534 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -51,17 +51,25 @@ }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", + "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", "p-throttle": "^4.1.1", + "@backstage/plugin-events-node": "workspace:^", + "@types/node-fetch": "^2.5.12", + "node-fetch": "^2.7.0", "uuid": "^11.0.0" }, "devDependencies": { + "@backstage/backend-common": "workspace:^", "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", + "@backstage/plugin-events-backend-test-utils": "workspace:^", "luxon": "^3.0.0", "msw": "^1.0.0" }, diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index 0da9b78139..c72733806b 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -5,14 +5,18 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; import { BitbucketServerIntegrationConfig } from '@backstage/integration'; +import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-node'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; -import { LocationSpec } from '@backstage/plugin-catalog-node'; +import { EventsService } from '@backstage/plugin-events-node'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; import { LoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api'; +import { TaskRunner } from '@backstage/backend-tasks'; +import { TokenManager } from '@backstage/backend-common'; // @public export class BitbucketServerClient { @@ -22,6 +26,11 @@ export class BitbucketServerClient { config: BitbucketServerIntegrationConfig; }): BitbucketServerClient; // (undocumented) + getDefaultBranch(options: { + projectKey: string; + repo: string; + }): Promise; + // (undocumented) getFile(options: { projectKey: string; repo: string; @@ -47,6 +56,16 @@ export class BitbucketServerClient { }; } +// @public (undocumented) +export type BitbucketServerDefaultBranch = { + id: string; + displayId: string; + type: string; + latestCommit: string; + latestChangeset: string; + isDefault: boolean; +}; + // @public export class BitbucketServerEntityProvider implements EntityProvider { connect(connection: EntityProviderConnection): Promise; @@ -55,9 +74,12 @@ export class BitbucketServerEntityProvider implements EntityProvider { config: Config, options: { logger: LoggerService; + events?: EventsService; parser?: BitbucketServerLocationParser; - schedule?: SchedulerServiceTaskRunner; - scheduler?: SchedulerService; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + catalogApi?: CatalogApi; + tokenManager?: TokenManager; }, ): BitbucketServerEntityProvider[]; getProviderName(): string; @@ -65,6 +87,52 @@ export class BitbucketServerEntityProvider implements EntityProvider { refresh(logger: LoggerService): Promise; } +// @public (undocumented) +export namespace BitbucketServerEvents { + // (undocumented) + export type Actor = { + name?: string; + id: number; + }; + // (undocumented) + export type Change = { + ref: { + id: string; + displayId: string; + type: string; + }; + }; + // (undocumented) + export interface Event { + // (undocumented) + eventKey: string; + } + // (undocumented) + export interface RefsChangedEvent extends Event { + // (undocumented) + actor: Actor; + // (undocumented) + changes: Change[]; + // (undocumented) + commits: undefined; + // (undocumented) + date: string; + // (undocumented) + repository: Repository; + // (undocumented) + ToCommit: undefined; + } + // (undocumented) + export type Repository = { + slug: string; + id: number; + name: string; + project: BitbucketServerProject; + }; + { + } +} + // @public (undocumented) export type BitbucketServerListOptions = { [key: string]: number | undefined; @@ -108,6 +176,7 @@ export type BitbucketServerRepository = { }[] >; archived: boolean; + defaultBranch: string; }; // @public (undocumented) diff --git a/plugins/catalog-backend-module-bitbucket-server/src/index.ts b/plugins/catalog-backend-module-bitbucket-server/src/index.ts index f05c9ca0e9..96e3a1a0a7 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/index.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/index.ts @@ -21,12 +21,7 @@ */ export { default } from './module'; -export { BitbucketServerClient } from './lib'; -export type { - BitbucketServerProject, - BitbucketServerRepository, - BitbucketServerPagedResponse, - BitbucketServerListOptions, -} from './lib'; +export * from './lib'; + export { BitbucketServerEntityProvider } from './providers'; export type { BitbucketServerLocationParser } from './providers'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.test.ts index 6abcede9de..fbb4054390 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.test.ts @@ -23,7 +23,12 @@ import { BitbucketServerPagedResponse, paginated, } from './BitbucketServerClient'; -import { BitbucketServerProject, BitbucketServerRepository } from './types'; +import { + BitbucketServerDefaultBranch, + BitbucketServerProject, + BitbucketServerRepository, +} from './types'; +import { NotFoundError } from '@backstage/errors'; const server = setupServer(); @@ -122,6 +127,7 @@ describe('BitbucketServerClient', () => { ], }, archived: false, + defaultBranch: 'master', }, ], }; @@ -200,6 +206,7 @@ describe('BitbucketServerClient', () => { ], }, archived: false, + defaultBranch: 'master', }; return res(ctx.json(response)); @@ -217,4 +224,82 @@ describe('BitbucketServerClient', () => { 'https://bitbucket.mycompany.com/projects/test-project', ); }); + + it('getRepository no repo', async () => { + server.use( + rest.get( + `${config.apiBaseUrl}/projects/test-project/repos/wrong-repo`, + (_, res, ctx) => { + return res(ctx.status(404)); + }, + ), + ); + + const error = async () => { + await client.getRepository({ + projectKey: 'test-project', + repo: 'wrong-repo', + }); + }; + + await expect(error).rejects.toThrow( + "Repository 'wrong-repo' in project 'test-project' does not exist.", + ); + await expect(error).rejects.toThrow(NotFoundError); + }); + + it('getDefaultBranch success', async () => { + server.use( + rest.get( + `${config.apiBaseUrl}/projects/test-project/repos/test-repo/default-branch`, + (req, res, ctx) => { + if ( + req.headers.get('authorization') !== + 'Basic dGVzdC11c2VyOnRlc3QtcHc=' + ) { + return res(ctx.status(400)); + } + const response: BitbucketServerDefaultBranch = { + id: 'refs/heads/master', + displayId: 'master', + type: 'BRANCH', + latestCommit: '0f2f3ae484054696568bf4560ba4da280f7df82a', + latestChangeset: '0f2f3ae484054696568bf4560ba4da280f7df82a', + isDefault: true, + }; + + return res(ctx.json(response)); + }, + ), + ); + + const repo = await client.getDefaultBranch({ + projectKey: 'test-project', + repo: 'test-repo', + }); + expect(repo.displayId).toEqual('master'); + }); + + it('getDefaultBranch endpoint', async () => { + server.use( + rest.get( + `${config.apiBaseUrl}/projects/test-project/repos/wrong-repo/default-branch`, + (_, res, ctx) => { + return res(ctx.status(404)); + }, + ), + ); + + const error = async () => { + await client.getDefaultBranch({ + projectKey: 'test-project', + repo: 'wrong-repo', + }); + }; + + await expect(error).rejects.toThrow( + "Your Bitbucket Server version no longer supports the default branch endpoint or 'wrong-repo' in 'test-project' does not exist.", + ); + await expect(error).rejects.toThrow(NotFoundError); + }); }); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts index 3dd61d64e7..ba5f75268c 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts @@ -18,7 +18,6 @@ import { BitbucketServerIntegrationConfig, getBitbucketServerRequestOptions, } from '@backstage/integration'; -import { BitbucketServerProject, BitbucketServerRepository } from './types'; import pThrottle from 'p-throttle'; // 1 per second @@ -32,6 +31,15 @@ const throttledFetch = throttle( return await fetch(url, options); }, ); +import { + BitbucketServerDefaultBranch, + BitbucketServerRepository, +} from './index'; + +import { BitbucketServerProject } from './types'; +import { NotFoundError } from '@backstage/errors'; + +import { ResponseError } from '@backstage/errors'; /** * A client for interacting with a Bitbucket Server instance @@ -92,7 +100,35 @@ export class BitbucketServerClient { request, getBitbucketServerRequestOptions(this.config), ); - return response.json(); + if (response.ok) { + return response.json(); + } + if (response.status === 404) { + throw new NotFoundError( + `Repository '${options.repo}' in project '${options.projectKey}' does not exist.`, + ); + } + throw await ResponseError.fromResponse(response); + } + + async getDefaultBranch(options: { + projectKey: string; + repo: string; + }): Promise { + const request = `${this.config.apiBaseUrl}/projects/${options.projectKey}/repos/${options.repo}/default-branch`; + const response = await fetch( + request, + getBitbucketServerRequestOptions(this.config), + ); + if (response.ok) { + return response.json(); + } + if (response.status === 404) { + throw new NotFoundError( + `Your Bitbucket Server version no longer supports the default branch endpoint or '${options.repo}' in '${options.projectKey}' does not exist.`, + ); + } + throw await ResponseError.fromResponse(response); } resolvePath(options: { projectKey: string; repo: string; path: string }): { @@ -163,7 +199,9 @@ export type BitbucketServerPagedResponse = { values: T[]; nextPageStart: number; }; - +/** + * @public + */ export async function* paginated( request: ( options: BitbucketServerListOptions, diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts index 8b72c9335b..14fb01629e 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts @@ -19,7 +19,4 @@ export type { BitbucketServerPagedResponse, BitbucketServerListOptions, } from './BitbucketServerClient'; -export type { - BitbucketServerRepository, - BitbucketServerProject, -} from './types'; +export * from './types'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts index 381eecb493..e014528791 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts @@ -15,7 +15,7 @@ */ /** @public */ -export type BitbucketServerRepository = { +type BitbucketServerRepository = { project: { key: string; }; @@ -28,9 +28,56 @@ export type BitbucketServerRepository = { }[] >; archived: boolean; + defaultBranch: string; }; /** @public */ -export type BitbucketServerProject = { +type BitbucketServerProject = { key: string; }; + +/** @public */ +type BitbucketServerDefaultBranch = { + id: string; + displayId: string; + type: string; + latestCommit: string; + latestChangeset: string; + isDefault: boolean; +}; + +/** @public */ +namespace BitbucketServerEvents { + interface Event { + eventKey: string; + } + + export interface RefsChangedEvent extends Event { + date: string; + actor: Actor; + repository: Repository; + changes: Change[]; + commits: undefined; + ToCommit: undefined; + } + type Actor = { + name?: string; + id: number; + }; + type Change = { + ref: { id: string; displayId: string; type: string }; + }; + type Repository = { + slug: string; + id: number; + name: string; + project: BitbucketServerProject; + }; +} + +export type { + BitbucketServerDefaultBranch, + BitbucketServerProject, + BitbucketServerEvents, + BitbucketServerRepository, +}; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts index fbd4fa7a36..27420e9300 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts @@ -16,20 +16,35 @@ import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api'; import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { createServiceFactory } from '@backstage/backend-plugin-api'; +import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; +import { Duration } from 'luxon'; import { catalogModuleBitbucketServerEntityProvider } from './catalogModuleBitbucketServerEntityProvider'; -import { BitbucketServerEntityProvider } from '../providers'; +import { BitbucketServerEntityProvider } from '../providers/BitbucketServerEntityProvider'; describe('catalogModuleBitbucketServerEntityProvider', () => { it('should register provider at the catalog extension point', async () => { + const events = new TestEventsService(); + const eventsServiceFactory = createServiceFactory({ + service: eventsServiceRef, + deps: {}, + async factory({}) { + return events; + }, + }); let addedProviders: Array | undefined; let usedSchedule: SchedulerServiceTaskScheduleDefinition | undefined; - const extensionPoint = { + const catalogExtensionPointImpl = { addEntityProvider: (providers: any) => { addedProviders = providers; }, }; + const connection = jest.fn() as unknown as EntityProviderConnection; const runner = jest.fn(); const scheduler = mockServices.scheduler.mock({ createScheduledTaskRunner(schedule) { @@ -60,7 +75,9 @@ describe('catalogModuleBitbucketServerEntityProvider', () => { }; await startTestBackend({ - extensionPoints: [[catalogProcessingExtensionPoint, extensionPoint]], + extensionPoints: [ + [catalogProcessingExtensionPoint, catalogExtensionPointImpl], + ], features: [ catalogModuleBitbucketServerEntityProvider, mockServices.rootConfig.factory({ data: config }), @@ -72,9 +89,14 @@ describe('catalogModuleBitbucketServerEntityProvider', () => { expect(usedSchedule?.frequency).toEqual({ months: 1 }); expect(usedSchedule?.timeout).toEqual({ minutes: 3 }); expect(addedProviders?.length).toEqual(1); - expect(addedProviders?.pop()?.getProviderName()).toEqual( + expect(runner).not.toHaveBeenCalled(); + const provider = addedProviders!.pop()!; + expect(provider.getProviderName()).toEqual( 'bitbucketServer-provider:default', ); - expect(runner).not.toHaveBeenCalled(); + await provider.connect(connection); + expect(events.subscribed).toHaveLength(1); + expect(events.subscribed[0].id).toEqual('bitbucketServer-provider:default'); + expect(runner).toHaveBeenCalledTimes(1); }); }); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts index 2c78d219d9..615bbb3477 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts @@ -18,8 +18,12 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; -import { BitbucketServerEntityProvider } from '../providers'; +import { + catalogProcessingExtensionPoint, + catalogServiceRef, +} from '@backstage/plugin-catalog-node/alpha'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; +import { BitbucketServerEntityProvider } from '../providers/BitbucketServerEntityProvider'; /** * @public @@ -31,14 +35,28 @@ export const catalogModuleBitbucketServerEntityProvider = createBackendModule({ env.registerInit({ deps: { catalog: catalogProcessingExtensionPoint, + catalogApi: catalogServiceRef, config: coreServices.rootConfig, + events: eventsServiceRef, logger: coreServices.logger, scheduler: coreServices.scheduler, + tokenManager: coreServices.tokenManager, }, - async init({ catalog, config, logger, scheduler }) { + async init({ + catalog, + catalogApi, + config, + events, + logger, + scheduler, + tokenManager, + }) { const providers = BitbucketServerEntityProvider.fromConfig(config, { + catalogApi, + events, logger, scheduler, + tokenManager, }); catalog.addEntityProvider(providers); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts index 05e0ea6a01..dd2735177b 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { TokenManager } from '@backstage/backend-common'; import { SchedulerService, SchedulerServiceTaskRunner, @@ -24,11 +25,22 @@ import { registerMswTestHooks, } from '@backstage/backend-test-utils'; import { ConfigReader } from '@backstage/config'; -import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; +import { + DeferredEntity, + EntityProviderConnection, + locationSpecToLocationEntity, +} from '@backstage/plugin-catalog-node'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; -import { BitbucketServerEntityProvider } from './BitbucketServerEntityProvider'; +import { + BitbucketServerEntityProvider, + toDeferredEntities, +} from './BitbucketServerEntityProvider'; import { BitbucketServerPagedResponse } from '../lib'; +import { Entity, LocationEntity } from '@backstage/catalog-model'; +import { BitbucketServerEvents } from '../lib/index'; +import { CatalogApi } from '@backstage/catalog-client'; +import { DefaultEventsService } from '@backstage/plugin-events-node'; class PersistingTaskRunner implements SchedulerServiceTaskRunner { private tasks: SchedulerServiceTaskInvocationDefinition[] = []; @@ -57,9 +69,15 @@ function pagedResponse(values: any): BitbucketServerPagedResponse { const logger = mockServices.logger.mock(); +const events = DefaultEventsService.create({ logger }); + const server = setupServer(); -function setupStubs(projects: Project[], baseUrl: string) { +function setupStubs( + projects: Project[], + baseUrl: string, + defaultBranch: string, +) { // Stub projects server.use( rest.get(`${baseUrl}/rest/api/1.0/projects`, (_, res, ctx) => { @@ -93,6 +111,7 @@ function setupStubs(projects: Project[], baseUrl: string) { ], }, archived: repo.archived ?? false, + defaultBranch: defaultBranch, }); } return res(ctx.json(pagedResponse(response))); @@ -102,6 +121,107 @@ function setupStubs(projects: Project[], baseUrl: string) { } } +const host = 'bitbucket.mycompany.com'; +const targetPath = `/catalog-info.yaml`; +const test1RepoUrl = `https://${host}/projects/TEST/repos/test1/browse`; + +function setupRepositoryReqHandler(defaultBranch: string) { + server.use( + rest.get( + `https://${host}/rest/api/1.0/projects/TEST/repos/test1`, + (_, res, ctx) => { + const response = { + slug: 'test1', + id: 1, + name: 'test1', + project: { + key: 'TEST', + id: 1, + name: 'TEST', + links: { + self: [ + { + href: `https://${host}/projects/TEST`, + }, + ], + }, + }, + links: { + self: [ + { + href: `${test1RepoUrl}`, + }, + ], + }, + defaultBranch: defaultBranch, + }; + return res(ctx.json(response)); + }, + ), + ); +} + +const tokenManager = { + getToken: async () => { + return { token: 'fake-token' }; + }, +} as any as TokenManager; +const repoPushEvent: BitbucketServerEvents.RefsChangedEvent = { + eventKey: 'repo:refs_changed', + date: '2017-09-19T09:45:32+1000', + actor: { + name: 'admin', + id: 1, + }, + repository: { + slug: 'test1', + id: 84, + name: 'test1', + project: { + key: 'TEST', + }, + }, + changes: [ + { + ref: { + id: 'refs/heads/master', + displayId: 'master', + type: 'BRANCH', + }, + }, + ], + commits: undefined, + ToCommit: undefined, +}; +const repoPushEventParams = { + topic: 'bitbucketServer.repo:refs_changed', + eventPayload: repoPushEvent, + metadata: { 'x-event-key': 'repo:refs_changed' }, +}; + +const createLocationEntity = ( + repoUrl: string, + path: string, + defaultBranch: string, +): LocationEntity => { + const target = `${repoUrl}${path}`; + + const entity = locationSpecToLocationEntity({ + location: { + type: 'url', + target: target, + presence: 'optional', + }, + }); + entity.metadata.annotations = { + ...entity.metadata.annotations, + [`${host}/repo-url`]: target, + ['bitbucket.org/default-branch']: defaultBranch, + }; + + return entity; +}; + describe('BitbucketServerEntityProvider', () => { registerMswTestHooks(server); afterEach(() => { @@ -157,6 +277,7 @@ describe('BitbucketServerEntityProvider', () => { }); const providers = BitbucketServerEntityProvider.fromConfig(config, { logger, + events, schedule, }); @@ -191,6 +312,7 @@ describe('BitbucketServerEntityProvider', () => { }); const providers = BitbucketServerEntityProvider.fromConfig(config, { logger, + events, schedule, }); @@ -204,7 +326,6 @@ describe('BitbucketServerEntityProvider', () => { }); it('apply full update on scheduled execution with filters', async () => { - const host = 'bitbucket.mycompany.com'; const config = new ConfigReader({ integrations: { bitbucketServer: [ @@ -253,6 +374,7 @@ describe('BitbucketServerEntityProvider', () => { { key: 'other-project', repos: [{ name: 'other-repo' }] }, ], `https://${host}`, + 'master', ); await provider.connect(entityProviderConnection); @@ -270,6 +392,7 @@ describe('BitbucketServerEntityProvider', () => { annotations: { 'backstage.io/managed-by-location': `url:${url}`, 'backstage.io/managed-by-origin-location': `url:${url}`, + 'bitbucket.org/default-branch': 'master', }, name: 'generated-77f4323822420990f8c3e3c981d38c2dec4ae3a6', }, @@ -291,7 +414,6 @@ describe('BitbucketServerEntityProvider', () => { }); it('apply full update on scheduled execution without filters', async () => { - const host = 'bitbucket.mycompany.com'; const config = new ConfigReader({ integrations: { bitbucketServer: [ @@ -329,6 +451,7 @@ describe('BitbucketServerEntityProvider', () => { { key: 'other-project', repos: [{ name: 'other-repo' }] }, ], `https://${host}`, + 'master', ); await provider.connect(entityProviderConnection); @@ -345,6 +468,7 @@ describe('BitbucketServerEntityProvider', () => { annotations: { 'backstage.io/managed-by-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', }, name: 'generated-77f4323822420990f8c3e3c981d38c2dec4ae3a6', }, @@ -364,6 +488,7 @@ describe('BitbucketServerEntityProvider', () => { annotations: { 'backstage.io/managed-by-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', }, name: 'generated-d8d4944c30c2906dfee172ddda9537f9893b2c0f', }, @@ -441,7 +566,6 @@ describe('BitbucketServerEntityProvider', () => { }); it('apply full update with schedule in config', async () => { - const host = 'bitbucket.mycompany.com'; const config = new ConfigReader({ integrations: { bitbucketServer: [ @@ -488,6 +612,7 @@ describe('BitbucketServerEntityProvider', () => { { key: 'other-project', repos: [{ name: 'other-repo' }] }, ], `https://${host}`, + 'master', ); await provider.connect(entityProviderConnection); @@ -504,6 +629,7 @@ describe('BitbucketServerEntityProvider', () => { annotations: { 'backstage.io/managed-by-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', }, name: 'generated-77f4323822420990f8c3e3c981d38c2dec4ae3a6', }, @@ -523,6 +649,7 @@ describe('BitbucketServerEntityProvider', () => { annotations: { 'backstage.io/managed-by-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', }, name: 'generated-d8d4944c30c2906dfee172ddda9537f9893b2c0f', }, @@ -542,4 +669,360 @@ describe('BitbucketServerEntityProvider', () => { entities: expectedEntities, }); }); + + it('Multiple location entities to deferred entities', async () => { + const schedule = new PersistingTaskRunner(); + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketServer: { + host: host, + }, + }, + }, + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + }); + const providers = BitbucketServerEntityProvider.fromConfig(config, { + logger, + events, + schedule, + }); + + expect(providers).toHaveLength(1); + expect(providers[0].getProviderName()).toEqual( + 'bitbucketServer-provider:default', + ); + + const locationEntities = [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + [`${host}/repo-url`]: `https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', + }, + name: 'generated-77f4323822420990f8c3e3c981d38c2dec4ae3a6', + }, + spec: { + presence: 'optional', + target: `https://${host}/projects/project-test/repos/repo-test/browse/catalog-info.yaml`, + type: 'url', + }, + }, + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + [`${host}/repo-url`]: `https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', + }, + name: 'generated-d8d4944c30c2906dfee172ddda9537f9893b2c0f', + }, + spec: { + presence: 'optional', + target: `https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + type: 'url', + }, + }, + ]; + + const deferredEntities = toDeferredEntities( + locationEntities, + providers[0].getProviderName(), + ); + + expect(deferredEntities).toEqual([ + { + locationKey: providers[0].getProviderName(), + entity: locationEntities[0], + }, + { + locationKey: providers[0].getProviderName(), + entity: locationEntities[1], + }, + ]); + }); + + it('refresh onRepoPush', async () => { + const schedule = new PersistingTaskRunner(); + const keptModule = createLocationEntity( + test1RepoUrl, + `/kept-module:${targetPath}`, + 'master', + ); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + setupRepositoryReqHandler('master'); + + const config = new ConfigReader({ + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + catalog: { + providers: { + bitbucketServer: { + mainProvider: { + host: host, + apiBaseUrl: `https://${host}/rest/api/1.0`, + catalogPath: `/kept-module:/catalog-info.yaml`, + defaultBranch: 'master', + }, + }, + }, + }, + }); + + const catalogApi = { + getEntities: async ( + request: { filter: Record }, + options: { token: string }, + ): Promise<{ items: Entity[] }> => { + if ( + options.token !== 'fake-token' || + request.filter.kind !== 'Location' || + request.filter[`metadata.annotations.${host}/repo-url`] !== + `${test1RepoUrl}/kept-module:${targetPath}` + ) { + return { items: [] }; + } + return { + items: [keptModule], + }; + }, + }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { + catalogApi: catalogApi as any as CatalogApi, + logger, + schedule, + events, + tokenManager, + })[0]; + + await provider.connect(entityProviderConnection); + await events.publish(repoPushEventParams); + + expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.refresh).toHaveBeenCalledWith({ + keys: [`url:${test1RepoUrl}/kept-module:${targetPath}`], + }); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(0); + }); + + it('no refresh onRepoPush due to different default branch', async () => { + const schedule = new PersistingTaskRunner(); + const keptModule = createLocationEntity( + test1RepoUrl, + `/kept-module:${targetPath}`, + 'main', + ); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + setupRepositoryReqHandler('main'); + + const config = new ConfigReader({ + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + catalog: { + providers: { + bitbucketServer: { + mainProvider: { + host: host, + apiBaseUrl: `https://${host}/rest/api/1.0`, + catalogPath: `/kept-module:/catalog-info.yaml`, + }, + }, + }, + }, + }); + + const catalogApi = { + getEntities: async ( + request: { filter: Record }, + options: { token: string }, + ): Promise<{ items: Entity[] }> => { + if ( + options.token !== 'fake-token' || + request.filter.kind !== 'Location' || + request.filter[`metadata.annotations.${host}/repo-url`] !== + `${test1RepoUrl}/kept-module:${targetPath}` + ) { + return { items: [] }; + } + return { + items: [keptModule], + }; + }, + }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { + catalogApi: catalogApi as any as CatalogApi, + logger, + schedule, + events, + tokenManager, + })[0]; + + await provider.connect(entityProviderConnection); + await events.publish(repoPushEventParams); + + expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(0); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(0); + }); + + it('add onRepoPush', async () => { + const schedule = new PersistingTaskRunner(); + setupRepositoryReqHandler('master'); + const addedModule = createLocationEntity( + test1RepoUrl, + `/added-module:${targetPath}`, + 'master', + ); + + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + const config = new ConfigReader({ + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + catalog: { + providers: { + bitbucketServer: { + mainProvider: { + host: host, + apiBaseUrl: `https://${host}/rest/api/1.0`, + catalogPath: `/added-module:/catalog-info.yaml`, + }, + }, + }, + }, + }); + + const catalogApi = { + getEntities: async ( + _request: { filter: Record }, + _options: { token: string }, + ): Promise<{ items: Entity[] }> => { + return { + items: [], + }; + }, + }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { + catalogApi: catalogApi as any as CatalogApi, + logger, + schedule, + events, + tokenManager, + })[0]; + + await provider.connect(entityProviderConnection); + await events.publish(repoPushEventParams); + const addedEntities = [ + { + entity: addedModule, + locationKey: 'bitbucketServer-provider:mainProvider', + }, + ]; + const removedEntities: DeferredEntity[] = []; + + expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.refresh).toHaveBeenCalledWith({ + keys: [], + }); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'delta', + added: addedEntities, + removed: removedEntities, + }); + }); + + it('fail add onRepoPush from wrong default branch', async () => { + const schedule = new PersistingTaskRunner(); + setupRepositoryReqHandler('main'); + + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + + const config = new ConfigReader({ + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + catalog: { + providers: { + bitbucketServer: { + mainProvider: { + host: host, + apiBaseUrl: `https://${host}/rest/api/1.0`, + catalogPath: `/added-module:/catalog-info.yaml`, + }, + }, + }, + }, + }); + + const catalogApi = { + getEntities: async ( + _request: { filter: Record }, + _options: { token: string }, + ): Promise<{ items: Entity[] }> => { + return { + items: [], + }; + }, + }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { + catalogApi: catalogApi as any as CatalogApi, + logger, + schedule, + events, + tokenManager, + })[0]; + + await provider.connect(entityProviderConnection); + await events.publish(repoPushEventParams); + + expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(0); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(0); + }); }); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index 46f492d64c..4afc02b8b5 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { Entity } from '@backstage/catalog-model'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; +import { Entity, LocationEntity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; import { @@ -24,6 +25,7 @@ import { import { EntityProvider, EntityProviderConnection, + DeferredEntity, } from '@backstage/plugin-catalog-node'; import * as uuid from 'uuid'; import { BitbucketServerClient, paginated } from '../lib'; @@ -40,6 +42,12 @@ import { SchedulerService, SchedulerServiceTaskRunner, } from '@backstage/backend-plugin-api'; +import { BitbucketServerEvents } from '../lib'; +import { EventsService } from '@backstage/plugin-events-node'; +import { CatalogApi } from '@backstage/catalog-client'; +import { TokenManager } from '@backstage/backend-common'; + +const TOPIC_REPO_REFS_CHANGED = 'bitbucketServer.repo:refs_changed'; /** * Discovers catalog files located in Bitbucket Server. @@ -56,14 +64,23 @@ export class BitbucketServerEntityProvider implements EntityProvider { private readonly logger: LoggerService; private readonly scheduleFn: () => Promise; private connection?: EntityProviderConnection; + private readonly catalogApi?: CatalogApi; + private readonly events?: EventsService; + private readonly tokenManager?: TokenManager; + private eventConfigErrorThrown = false; + private readonly targetAnnotation: string; + private readonly defaultBranchAnnotation: string; static fromConfig( config: Config, options: { logger: LoggerService; + events?: EventsService; parser?: BitbucketServerLocationParser; - schedule?: SchedulerServiceTaskRunner; - scheduler?: SchedulerService; + schedule?: TaskRunner; + scheduler?: PluginTaskScheduler; + catalogApi?: CatalogApi; + tokenManager?: TokenManager; }, ): BitbucketServerEntityProvider[] { const integrations = ScmIntegrations.fromConfig(config); @@ -98,6 +115,9 @@ export class BitbucketServerEntityProvider implements EntityProvider { options.logger, taskRunner, options.parser, + options.catalogApi, + options.events, + options.tokenManager, ); }); } @@ -108,6 +128,9 @@ export class BitbucketServerEntityProvider implements EntityProvider { logger: LoggerService, taskRunner: SchedulerServiceTaskRunner, parser?: BitbucketServerLocationParser, + catalogApi?: CatalogApi, + events?: EventsService, + tokenManager?: TokenManager, ) { this.integration = integration; this.config = config; @@ -116,6 +139,11 @@ export class BitbucketServerEntityProvider implements EntityProvider { target: this.getProviderName(), }); this.scheduleFn = this.createScheduleFn(taskRunner); + this.catalogApi = catalogApi; + this.tokenManager = tokenManager; + this.targetAnnotation = `${this.config.host.split(':')[0]}/repo-url`; + this.defaultBranchAnnotation = 'bitbucket.org/default-branch'; + this.events = events; } private createScheduleFn( @@ -154,6 +182,22 @@ export class BitbucketServerEntityProvider implements EntityProvider { async connect(connection: EntityProviderConnection): Promise { this.connection = connection; await this.scheduleFn(); + + if (this.events) { + await this.events.subscribe({ + id: this.getProviderName(), + topics: [TOPIC_REPO_REFS_CHANGED], + onEvent: async params => { + if (params.topic !== TOPIC_REPO_REFS_CHANGED) { + return; + } + + await this.onRepoPush( + params.eventPayload as BitbucketServerEvents.RefsChangedEvent, + ); + }, + }); + } } async refresh(logger: LoggerService) { @@ -218,10 +262,373 @@ export class BitbucketServerEntityProvider implements EntityProvider { presence: 'optional', }, })) { + if (entity.metadata.annotations === undefined) { + entity.metadata.annotations = {}; + } + if (repository.defaultBranch === undefined) { + const defaultBranchResponse = await client.getDefaultBranch({ + repo: repository.slug, + projectKey: project.key, + }); + entity.metadata.annotations[this.defaultBranchAnnotation] = + defaultBranchResponse.displayId; + } else { + entity.metadata.annotations[this.defaultBranchAnnotation] = + repository.defaultBranch; + } result.push(entity); } } } return result; } + + /** + * Checks if the webhook was triggered on a commit to the head branch of a repository + * @param event Bitbucket Server webhook repo:refs_changed event + */ + private isDefaultBranchPush( + defaultBranch: String, + event: BitbucketServerEvents.RefsChangedEvent, + ): boolean { + return event.changes.some(c => defaultBranch === c.ref.displayId); + } + + /** + * Checks if the provider is able to handle events + * @returns Boolean + */ + private canHandleEvents(): boolean { + if ( + this.catalogApi !== undefined && + this.catalogApi !== null && + this.tokenManager !== undefined && + this.tokenManager !== null + ) { + return true; + } + + if (!this.eventConfigErrorThrown) { + this.eventConfigErrorThrown = true; + throw new Error( + `${this.getProviderName()} not well configured to handle repo:push. Missing CatalogApi and/or TokenManager.`, + ); + } + + return false; + } + + /** + * Creates a Bitbucket Server location entity for the repository that is referenced in the push event passed in. + * + * @param event A Bitbucket Server push event with repository information. + * + * @returns An array of location entities for the repository. + * + * @example + * + * const RefsChangedEvent = { + * "eventKey": "repo:refs_changed", + * "date": "2022-01-01T00:00:00Z", + * "actor": { + * "name": "johndoe", + * "emailAddress": "johndoe@example.com", + * "id": 123, + * "displayName": "John Doe", + * "active": true, + * "slug": "johndoe", + * "type": "NORMAL" + * }, + * "repository": { + * "slug": "my-repo", + * "id": 123, + * "name": "My Repository", + * "project": { + * "key": "my-project", + * "id": 456, + * "name": "My Project", + * "description": "My project description", + * "public": true, + * "type": "NORMAL" + * } + * }, + * "changes": [ + * { + * "ref": { + * "id": "refs/heads/master", + * "displayId": "master", + * "type": "BRANCH" + * }, + * "refId": "refs/heads/master", + * "fromHash": "0123456789abcdef0123456789abcdef0123456", + * "toHash": "fedcba9876543210fedcba9876543210fedcba9", + * "type": "UPDATE" + * } + * ] + * }; + * + * const locationEntities = await getLocationEntity(RefsChangedEvent); + * + * // locationEntities: + * // [ + * // { + * // kind: 'Location', + * // metadata: { + * // name: 'my-repo', + * // namespace: 'my-project', + * // annotations: { + * // 'backstage.io/managed-by-location': 'url/catalog-info-path', + * // 'backstage.io/managed-by-origin-location': 'url/catalog-info-path', + * // 'host/repo-url': 'url', + * // }, + * // }, + * // spec: { + * // type: 'bitbucket', + * // target: 'url/catalog-info-path', + * // presence: 'optional', + * // }, + * // }, + * // ] + */ + private async getLocationEntity( + event: BitbucketServerEvents.RefsChangedEvent, + ): Promise { + const client = BitbucketServerClient.fromConfig({ + config: this.integration.config, + }); + const result: Entity[] = []; + try { + const repository = await client.getRepository({ + projectKey: event.repository.project.key, + repo: event.repository.slug, + }); + + for await (const entity of this.parser({ + client, + logger: this.logger, + location: { + type: 'url', + target: `${repository.links.self[0].href}${this.config.catalogPath}`, + presence: 'optional', + }, + })) { + entity.metadata.annotations![ + this.targetAnnotation + ] = `${repository.links.self[0].href}${this.config.catalogPath}`; + + if (entity.metadata.annotations === undefined) { + entity.metadata.annotations = {}; + } + + if (repository.defaultBranch === undefined) { + const defaultBranchResponse = await client.getDefaultBranch({ + repo: repository.slug, + projectKey: event.repository.project.key, + }); + entity.metadata.annotations[this.defaultBranchAnnotation] = + defaultBranchResponse.displayId; + } else { + entity.metadata.annotations[this.defaultBranchAnnotation] = + repository.defaultBranch; + } + result.push(entity); + } + } catch (error: any) { + if (error.name === 'NotFoundError') { + this.logger.error(error.message); + } + } + + return result; + } + + /** + * Finds if there are existing location entities for the repository that was pushed. If there are, it simply refreshes those entities, + * if not, it discovers any entity that was added and removed in the list of entities + * @param event - A Bitbucket Server webhook event for repo:refs_change + */ + private async onRepoPush( + event: BitbucketServerEvents.RefsChangedEvent, + ): Promise { + if (!this.canHandleEvents()) { + this.logger.error( + 'Bitbucket Server catalog entity provider is not set up to handle events. Missing tokenManager or catalogApi.', + ); + return; + } + + if (!this.connection) { + throw new Error('Not initialized'); + } + + const repoSlug = event.repository.slug; + const catalogRepoUrl: string = `https://${this.config.host}/projects/${event.repository.project.key}/repos/${repoSlug}/browse${this.config.catalogPath}`; + this.logger.info(`handle repo:push event for ${catalogRepoUrl}`); + const targets = await this.getLocationEntity(event); + if (targets.length === 0) { + this.logger.error('Failed to create location entity.'); + return; + } + const { token } = await this.tokenManager!.getToken(); + const existing = await this.findExistingLocations(catalogRepoUrl, token); + const stillExisting: LocationEntity[] = []; + const removed: DeferredEntity[] = []; + existing.forEach(item => { + if ( + targets.find( + value => + value.metadata.annotations![this.targetAnnotation] === + item.spec.target, + ) + ) { + stillExisting.push(item); + } else { + removed.push({ + locationKey: this.getProviderName(), + entity: item, + }); + } + }); + + const added = await this.getAddedEntities(targets, existing); + + if ( + stillExisting.length > 0 && + stillExisting[0].metadata.annotations![this.defaultBranchAnnotation] !== + undefined && + !this.isDefaultBranchPush( + stillExisting[0].metadata.annotations![this.defaultBranchAnnotation], + event, + ) + ) { + return; + } else if ( + added.length > 0 && + added[0].entity.metadata.annotations![this.defaultBranchAnnotation] !== + undefined && + !this.isDefaultBranchPush( + added[0].entity.metadata.annotations![this.defaultBranchAnnotation], + event, + ) + ) { + return; + } else if ( + removed.length > 0 && + removed[0].entity.metadata.annotations![this.defaultBranchAnnotation] !== + undefined && + !this.isDefaultBranchPush( + removed[0].entity.metadata.annotations![this.defaultBranchAnnotation], + event, + ) + ) { + return; + } + + const promises: Promise[] = [ + this.connection.refresh({ + keys: stillExisting.map(entity => `url:${entity.spec.target}`), + }), + ]; + + if (added.length > 0 || removed.length > 0) { + promises.push( + this.connection.applyMutation({ + type: 'delta', + added: added, + removed: removed, + }), + ); + } + + await Promise.all(promises); + + return; + } + + /** + * Gets the location entities that are to be newly added to the catalog. + * @param targets Location entities for catalog files in the repository that was pushed + * @param existing The location entities in the repository that was pushed that already exist + * @returns Returns all deferred entities that represent location entities that don't exist in the catalog yet + */ + private async getAddedEntities( + targets: Entity[], + existing: LocationEntity[], + ): Promise { + const added: DeferredEntity[] = toDeferredEntities( + targets.filter( + target => + !existing.find( + item => + item.spec.target === + target.metadata.annotations![this.targetAnnotation], + ), + ), + this.getProviderName(), + ); + return added; + } + + /** + * Finds all location entities in the catalog that already have the annotation `metadata.annotations.${this.config.host}/repo-url` + * that is equivalent to @param repoURL\. + * @param repoURL URL for the reposity that the method finds the existing location entities for + * @param token Token from class token manager + */ + private async findExistingLocations( + catalogRepoUrl: string, + token: string, + ): Promise { + const filter: Record = {}; + filter.kind = 'Location'; + filter[`metadata.annotations.${this.targetAnnotation}`] = catalogRepoUrl; + + return this.catalogApi!.getEntities({ filter }, { token }).then( + result => result.items, + ) as Promise; + } + + // private static toLocationSpec(target: string): LocationSpec { + // return { + // type: 'url', + // target: target, + // presence: 'required', + // }; + // } +} + +/** + * Converts an array of entities into an array of deferred entities with the provider's name as the location key. + * + * @param targets An array of entities to convert. + * + * @returns An array of deferred entities with the provider's name as the location key. + * + * @example + * + * const entities = [ + * { kind: 'Component', namespace: 'default', name: 'my-component' }, + * { kind: 'System', namespace: 'default', name: 'my-system' }, + * { kind: 'API', namespace: 'default', name: 'my-api' }, + * ]; + * + * const deferredEntities = toDeferredEntities(entities); + * + * // deferredEntities: + * // [ + * // { locationKey: 'my-provider', entity: { kind: 'Component', namespace: 'default', name: 'my-component' } }, + * // { locationKey: 'my-provider', entity: { kind: 'System', namespace: 'default', name: 'my-system' } }, + * // { locationKey: 'my-provider', entity: { kind: 'API', namespace: 'default', name: 'my-api' } }, + * // ] + */ +export function toDeferredEntities( + targets: Entity[], + locationKey: string, +): DeferredEntity[] { + return targets.map(entity => { + return { + locationKey, + entity, + }; + }); } diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts index 018ff23069..3b921b6bab 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts @@ -48,10 +48,8 @@ export function readProviderConfigs( // simple/single config variant return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)]; } - return providersConfig.keys().map(id => { const providerConfig = providersConfig.getConfig(id); - return readProviderConfig(id, providerConfig); }); } diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerLocationParser.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerLocationParser.ts index 6aea23387e..1c48d5e7bd 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerLocationParser.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerLocationParser.ts @@ -14,13 +14,11 @@ * limitations under the License. */ -import { - LocationSpec, - locationSpecToLocationEntity, -} from '@backstage/plugin-catalog-node'; +import { locationSpecToLocationEntity } from '@backstage/plugin-catalog-node'; import { Entity } from '@backstage/catalog-model'; -import { BitbucketServerClient } from '../lib'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { LocationSpec } from '@backstage/plugin-catalog-common'; +import { BitbucketServerClient } from '../lib'; /** * A custom callback that reacts to finding a location by yielding entities. diff --git a/plugins/events-backend-module-bitbucket-server/.eslintrc.js b/plugins/events-backend-module-bitbucket-server/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/events-backend-module-bitbucket-server/README.md b/plugins/events-backend-module-bitbucket-server/README.md new file mode 100644 index 0000000000..2c8fdff092 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/README.md @@ -0,0 +1,48 @@ +# events-backend-module-bitbucket-server + +Welcome to the `events-backend-module-bitbucket-server` backend plugin! + +This package is a module for the `events-backend` backend plugin +and extends the event system with an `BitbucketServerEventRouter`. + +The event router will subscribe to the topic `bitbucketServer` +and route the events to more concrete topics based on the value +of the provided `x-event-key` metadata field. + +Examples: + +| x-event-key | topic | +| ------------------- | ----------------------------------- | +| `repo:refs_changed` | `bitbucketServer.repo:refs_changed` | +| `repo:modified` | `bitbucketServer.repo:modified` | + +Please find all possible webhook event types at the +[official documentation](https://confluence.atlassian.com/bitbucketserver/event-payload-938025882.html). + +## Installation + +Install the [`events-backend` plugin](../events-backend/README.md). + +Install this module: + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-events-backend-module-bitbucket-server +``` + +```ts +// packages/backend/src/index.ts +backend.add( + import('@backstage/plugin-events-backend-module-bitbucket-server/alpha'), +); +``` + +### Legacy Backend System + +```ts +// packages/backend/src/plugins/events.ts +const eventRouter = new BitbucketCloudEventRouter({ + events: env.events, +}); +await eventRouter.subscribe(); +``` diff --git a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md b/plugins/events-backend-module-bitbucket-server/api-report-alpha.md new file mode 100644 index 0000000000..62fb3e2a6b --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/api-report-alpha.md @@ -0,0 +1,14 @@ +## API Report File for "@backstage/plugin-events-backend-module-bitbucket-server" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; + +// @alpha +const eventsModuleBitbucketServerEventRouter: () => BackendFeature; +export default eventsModuleBitbucketServerEventRouter; +export { eventsModuleBitbucketServerEventRouter }; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/events-backend-module-bitbucket-server/api-report.md b/plugins/events-backend-module-bitbucket-server/api-report.md new file mode 100644 index 0000000000..18292a0d11 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/api-report.md @@ -0,0 +1,18 @@ +## API Report File for "@backstage/plugin-events-backend-module-bitbucket-server" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { EventParams } from '@backstage/plugin-events-node'; +import { EventsService } from '@backstage/plugin-events-node'; +import { SubTopicEventRouter } from '@backstage/plugin-events-node'; + +// @public +export class BitbucketServerEventRouter extends SubTopicEventRouter { + constructor(options: { events: EventsService }); + // (undocumented) + protected determineSubTopic(params: EventParams): string | undefined; + // (undocumented) + protected getSubscriberId(): string; +} +``` diff --git a/plugins/events-backend-module-bitbucket-server/catalog-info.yaml b/plugins/events-backend-module-bitbucket-server/catalog-info.yaml new file mode 100644 index 0000000000..d2a04ca2a8 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-events-backend-module-bitbucket-server + title: '@backstage/plugin-events-backend-module-bitbucket-server' +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json new file mode 100644 index 0000000000..025f559b44 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -0,0 +1,55 @@ +{ + "name": "@backstage/plugin-events-backend-module-bitbucket-server", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/events-backend-module-bitbucket-server" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/plugin-events-node": "workspace:^" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/plugin-events-backend-test-utils": "workspace:^", + "supertest": "^6.1.3" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/events-backend-module-bitbucket-server/src/alpha.ts b/plugins/events-backend-module-bitbucket-server/src/alpha.ts new file mode 100644 index 0000000000..c741a37f24 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/alpha.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 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 { eventsModuleBitbucketServerEventRouter } from './service/eventsModuleBitbucketServerEventRouter'; +export { eventsModuleBitbucketServerEventRouter as default } from './service/eventsModuleBitbucketServerEventRouter'; diff --git a/plugins/events-backend-module-bitbucket-server/src/index.ts b/plugins/events-backend-module-bitbucket-server/src/index.ts new file mode 100644 index 0000000000..78c97808a4 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2020 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. + */ + +/** + * The module "bitbucket-server" for the Backstage backend plugin "events-backend" + * adding an event router for Bitbucket Server. + * + * @packageDocumentation + */ + +export { BitbucketServerEventRouter } from './router/BitbucketServerEventRouter'; diff --git a/plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.test.ts b/plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.test.ts new file mode 100644 index 0000000000..6fdb4020f3 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.test.ts @@ -0,0 +1,53 @@ +/* + * 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. + */ + +import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; +import { BitbucketServerEventRouter } from './BitbucketServerEventRouter'; + +describe('BitbucketServerEventRouter', () => { + const events = new TestEventsService(); + const eventRouter = new BitbucketServerEventRouter({ events }); + const topic = 'bitbucketServer'; + const eventPayload = { test: 'payload' }; + const metadata = { 'x-event-key': 'test:type' }; + + beforeEach(() => { + events.reset(); + }); + + it('subscribed to topic', () => { + eventRouter.subscribe(); + + expect(events.subscribed).toHaveLength(1); + expect(events.subscribed[0].id).toEqual('BitbucketServerEventRouter'); + expect(events.subscribed[0].topics).toEqual([topic]); + }); + + it('no x-event-key', () => { + eventRouter.onEvent({ topic, eventPayload }); + + expect(events.published).toEqual([]); + }); + + it('with x-event-key', () => { + eventRouter.onEvent({ topic, eventPayload, metadata }); + + expect(events.published.length).toBe(1); + expect(events.published[0].topic).toEqual('bitbucketServer.test:type'); + expect(events.published[0].eventPayload).toEqual(eventPayload); + expect(events.published[0].metadata).toEqual(metadata); + }); +}); diff --git a/plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.ts b/plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.ts new file mode 100644 index 0000000000..45031cc5f3 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/router/BitbucketServerEventRouter.ts @@ -0,0 +1,45 @@ +/* + * 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. + */ + +import { + EventParams, + EventsService, + SubTopicEventRouter, +} from '@backstage/plugin-events-node'; + +/** + * Subscribes to the generic `bitbucketServer` topic + * and publishes the events under the more concrete sub-topic + * depending on the `x-event-key` provided. + * + * @public + */ +export class BitbucketServerEventRouter extends SubTopicEventRouter { + constructor(options: { events: EventsService }) { + super({ + events: options.events, + topic: 'bitbucketServer', + }); + } + + protected getSubscriberId(): string { + return 'BitbucketServerEventRouter'; + } + + protected determineSubTopic(params: EventParams): string | undefined { + return params.metadata?.['x-event-key'] as string | undefined; + } +} diff --git a/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts new file mode 100644 index 0000000000..60fc67eb4f --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts @@ -0,0 +1,44 @@ +/* + * 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. + */ + +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; +import { BitbucketServerEventRouter } from '../router/BitbucketServerEventRouter'; + +/** + * Module for the events-backend plugin, adding an event router for Bitbucket Server. + * + * Registers the `BitbucketServerEventRouter`. + * + * @alpha + */ +export const eventsModuleBitbucketServerEventRouter = createBackendModule({ + pluginId: 'events', + moduleId: 'bitbucket-server-event-router', + register(env) { + env.registerInit({ + deps: { + events: eventsServiceRef, + }, + async init({ events }) { + const eventRouter = new BitbucketServerEventRouter({ + events, + }); + await eventRouter.subscribe(); + }, + }); + }, +}); diff --git a/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts new file mode 100644 index 0000000000..56e158b8ab --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts @@ -0,0 +1,44 @@ +/* + * 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. + */ + +import { createServiceFactory } from '@backstage/backend-plugin-api'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; +import { eventsModuleBitbucketServerEventRouter } from './eventsModuleBitbucketServerEventRouter'; + +describe('eventsModuleBitbucketServerEventRouter', () => { + it('should be correctly wired and set up', async () => { + const events = new TestEventsService(); + const eventsServiceFactory = createServiceFactory({ + service: eventsServiceRef, + deps: {}, + async factory({}) { + return events; + }, + }); + + await startTestBackend({ + features: [ + eventsServiceFactory(), + eventsModuleBitbucketServerEventRouter(), + ], + }); + + expect(events.subscribed).toHaveLength(1); + expect(events.subscribed[0].id).toEqual('BitbucketServerEventRouter'); + }); +}); diff --git a/plugins/events-backend-module-bitbucket-server/src/setupTests.ts b/plugins/events-backend-module-bitbucket-server/src/setupTests.ts new file mode 100644 index 0000000000..d3232290a7 --- /dev/null +++ b/plugins/events-backend-module-bitbucket-server/src/setupTests.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2020 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 {}; diff --git a/yarn.lock b/yarn.lock index b337860101..42754c71b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5739,11 +5739,13 @@ __metadata: dependencies: "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" + "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" luxon: ^3.0.0 msw: ^1.0.0 @@ -6557,6 +6559,19 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-events-backend-module-bitbucket-server@workspace:plugins/events-backend-module-bitbucket-server": + version: 0.0.0-use.local + resolution: "@backstage/plugin-events-backend-module-bitbucket-server@workspace:plugins/events-backend-module-bitbucket-server" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/plugin-events-backend-test-utils": "workspace:^" + "@backstage/plugin-events-node": "workspace:^" + supertest: ^6.1.3 + languageName: unknown + linkType: soft + "@backstage/plugin-events-backend-module-gerrit@workspace:plugins/events-backend-module-gerrit": version: 0.0.0-use.local resolution: "@backstage/plugin-events-backend-module-gerrit@workspace:plugins/events-backend-module-gerrit" From 02e6dd48e9a37e2b77bacd76c5499f6c543513b8 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Wed, 19 Jun 2024 00:47:06 -0400 Subject: [PATCH 02/16] Fixed catalog backend tests and move backend-common to dependencies Signed-off-by: David Lilienfeld --- .../package.json | 2 +- ...oduleBitbucketServerEntityProvider.test.ts | 47 +++++++++---------- .../BitbucketServerEntityProvider.ts | 4 +- .../api-report-alpha.md | 4 +- .../package.json | 44 ++++++++--------- yarn.lock | 26 +++++++++- 6 files changed, 76 insertions(+), 51 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index a7c403b534..d31a3fc87d 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -50,6 +50,7 @@ "test": "backstage-cli package test" }, "dependencies": { + "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-client": "workspace:^", @@ -66,7 +67,6 @@ "uuid": "^11.0.0" }, "devDependencies": { - "@backstage/backend-common": "workspace:^", "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts index 27420e9300..e389f430d7 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts @@ -22,7 +22,6 @@ import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; import { eventsServiceRef } from '@backstage/plugin-events-node'; -import { Duration } from 'luxon'; import { catalogModuleBitbucketServerEntityProvider } from './catalogModuleBitbucketServerEntityProvider'; import { BitbucketServerEntityProvider } from '../providers/BitbucketServerEntityProvider'; @@ -53,35 +52,35 @@ describe('catalogModuleBitbucketServerEntityProvider', () => { }, }); - const config = { - catalog: { - providers: { - bitbucketServer: { - host: 'bitbucket.mycompany.com', - schedule: { - frequency: 'P1M', - timeout: 'PT3M', - }, - }, - }, - }, - integrations: { - bitbucketServer: [ - { - host: 'bitbucket.mycompany.com', - }, - ], - }, - }; - await startTestBackend({ extensionPoints: [ [catalogProcessingExtensionPoint, catalogExtensionPointImpl], ], features: [ + eventsServiceFactory(), catalogModuleBitbucketServerEntityProvider, - mockServices.rootConfig.factory({ data: config }), - mockServices.logger.factory(), + mockServices.rootConfig.factory({ + data: { + catalog: { + providers: { + bitbucketServer: { + host: 'bitbucket.mycompany.com', + schedule: { + frequency: 'P1M', + timeout: 'PT3M', + }, + }, + }, + }, + integrations: { + bitbucketServer: [ + { + host: 'bitbucket.mycompany.com', + }, + ], + }, + }, + }), scheduler.factory, ], }); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index 4afc02b8b5..bf4c895121 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -77,8 +77,8 @@ export class BitbucketServerEntityProvider implements EntityProvider { logger: LoggerService; events?: EventsService; parser?: BitbucketServerLocationParser; - schedule?: TaskRunner; - scheduler?: PluginTaskScheduler; + schedule?: SchedulerServiceTaskRunner; + scheduler?: SchedulerService; catalogApi?: CatalogApi; tokenManager?: TokenManager; }, diff --git a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md b/plugins/events-backend-module-bitbucket-server/api-report-alpha.md index 62fb3e2a6b..2eb8fc6075 100644 --- a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md +++ b/plugins/events-backend-module-bitbucket-server/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; +import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; // @alpha -const eventsModuleBitbucketServerEventRouter: () => BackendFeature; +const eventsModuleBitbucketServerEventRouter: BackendFeatureCompat; export default eventsModuleBitbucketServerEventRouter; export { eventsModuleBitbucketServerEventRouter }; diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json index 025f559b44..4f38cb1f05 100644 --- a/plugins/events-backend-module-bitbucket-server/package.json +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -1,17 +1,28 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-server", "version": "0.1.0", - "main": "src/index.ts", - "types": "src/index.ts", - "license": "Apache-2.0", + "backstage": { + "role": "backend-plugin-module", + "pluginId": "events", + "pluginPackage": "@backstage/plugin-events-backend" + }, "publishConfig": { "access": "public" }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/events-backend-module-bitbucket-server" + }, + "license": "Apache-2.0", "exports": { ".": "./src/index.ts", "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, + "main": "src/index.ts", + "types": "src/index.ts", "typesVersions": { "*": { "alpha": [ @@ -22,22 +33,17 @@ ] } }, - "repository": { - "type": "git", - "url": "https://github.com/backstage/backstage", - "directory": "plugins/events-backend-module-bitbucket-server" - }, - "backstage": { - "role": "backend-plugin-module" - }, + "files": [ + "dist" + ], "scripts": { - "start": "backstage-cli package start", "build": "backstage-cli package build", - "lint": "backstage-cli package lint", - "test": "backstage-cli package test", "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", "prepack": "backstage-cli package prepack", - "postpack": "backstage-cli package postpack" + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" }, "dependencies": { "@backstage/backend-plugin-api": "workspace:^", @@ -46,10 +52,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/plugin-events-backend-test-utils": "workspace:^", - "supertest": "^6.1.3" - }, - "files": [ - "dist" - ] + "@backstage/plugin-events-backend-test-utils": "workspace:^" + } } diff --git a/yarn.lock b/yarn.lock index 42754c71b9..dd86d50acc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3794,6 +3794,29 @@ __metadata: languageName: unknown linkType: soft +"@backstage/backend-tasks@workspace:^, @backstage/backend-tasks@workspace:packages/backend-tasks": + version: 0.0.0-use.local + resolution: "@backstage/backend-tasks@workspace:packages/backend-tasks" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/types": "workspace:^" + "@opentelemetry/api": ^1.3.0 + "@types/luxon": ^3.0.0 + cron: ^3.0.0 + knex: ^3.0.0 + lodash: ^4.17.21 + luxon: ^3.0.0 + uuid: ^9.0.0 + wait-for-expect: ^3.0.2 + zod: ^3.22.4 + languageName: unknown + linkType: soft + "@backstage/backend-test-utils@workspace:^, @backstage/backend-test-utils@workspace:packages/backend-test-utils": version: 0.0.0-use.local resolution: "@backstage/backend-test-utils@workspace:packages/backend-test-utils" @@ -5737,7 +5760,9 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend-module-bitbucket-server@workspace:plugins/catalog-backend-module-bitbucket-server" dependencies: + "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-tasks": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" @@ -6568,7 +6593,6 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/plugin-events-backend-test-utils": "workspace:^" "@backstage/plugin-events-node": "workspace:^" - supertest: ^6.1.3 languageName: unknown linkType: soft From 9463dfeda0d248de8837c5b17e251f911e6bf913 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Tue, 20 Aug 2024 23:55:13 -0400 Subject: [PATCH 03/16] Fix unused imports and api report Signed-off-by: David Lilienfeld --- .../catalog-backend-module-bitbucket-server/report.api.md | 5 ++--- .../catalogModuleBitbucketServerEntityProvider.test.ts | 1 - .../src/providers/BitbucketServerEntityProvider.ts | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index c72733806b..040626a83d 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -15,7 +15,6 @@ import { LocationSpec } from '@backstage/plugin-catalog-common'; import { LoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api'; -import { TaskRunner } from '@backstage/backend-tasks'; import { TokenManager } from '@backstage/backend-common'; // @public @@ -76,8 +75,8 @@ export class BitbucketServerEntityProvider implements EntityProvider { logger: LoggerService; events?: EventsService; parser?: BitbucketServerLocationParser; - schedule?: TaskRunner; - scheduler?: PluginTaskScheduler; + schedule?: SchedulerServiceTaskRunner; + scheduler?: SchedulerService; catalogApi?: CatalogApi; tokenManager?: TokenManager; }, diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts index e389f430d7..4ae6ff3aae 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts @@ -17,7 +17,6 @@ import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api'; import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { createServiceFactory } from '@backstage/backend-plugin-api'; -import { TaskScheduleDefinition } from '@backstage/backend-tasks'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index bf4c895121..aa7c3d5863 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; import { Entity, LocationEntity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; From 71c0954ab417cd91cd1ab5e811410c7ddc28c75a Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Mon, 26 Aug 2024 14:04:46 -0400 Subject: [PATCH 04/16] Remove tokenmanager from module Signed-off-by: David Lilienfeld --- ...atalogModuleBitbucketServerEntityProvider.test.ts | 2 +- .../catalogModuleBitbucketServerEntityProvider.ts | 12 +----------- .../api-report-alpha.md | 4 ++-- .../eventsModuleBitbucketServerRouter.test.ts | 5 +---- 4 files changed, 5 insertions(+), 18 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts index 4ae6ff3aae..f9345170ed 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.test.ts @@ -56,7 +56,7 @@ describe('catalogModuleBitbucketServerEntityProvider', () => { [catalogProcessingExtensionPoint, catalogExtensionPointImpl], ], features: [ - eventsServiceFactory(), + eventsServiceFactory, catalogModuleBitbucketServerEntityProvider, mockServices.rootConfig.factory({ data: { diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts index 615bbb3477..a829428296 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts @@ -40,23 +40,13 @@ export const catalogModuleBitbucketServerEntityProvider = createBackendModule({ events: eventsServiceRef, logger: coreServices.logger, scheduler: coreServices.scheduler, - tokenManager: coreServices.tokenManager, }, - async init({ - catalog, - catalogApi, - config, - events, - logger, - scheduler, - tokenManager, - }) { + async init({ catalog, catalogApi, config, events, logger, scheduler }) { const providers = BitbucketServerEntityProvider.fromConfig(config, { catalogApi, events, logger, scheduler, - tokenManager, }); catalog.addEntityProvider(providers); diff --git a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md b/plugins/events-backend-module-bitbucket-server/api-report-alpha.md index 2eb8fc6075..3c4ce45a1f 100644 --- a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md +++ b/plugins/events-backend-module-bitbucket-server/api-report-alpha.md @@ -3,10 +3,10 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeatureCompat } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; // @alpha -const eventsModuleBitbucketServerEventRouter: BackendFeatureCompat; +const eventsModuleBitbucketServerEventRouter: BackendFeature; export default eventsModuleBitbucketServerEventRouter; export { eventsModuleBitbucketServerEventRouter }; diff --git a/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts index 56e158b8ab..a66c000089 100644 --- a/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts +++ b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerRouter.test.ts @@ -32,10 +32,7 @@ describe('eventsModuleBitbucketServerEventRouter', () => { }); await startTestBackend({ - features: [ - eventsServiceFactory(), - eventsModuleBitbucketServerEventRouter(), - ], + features: [eventsServiceFactory, eventsModuleBitbucketServerEventRouter], }); expect(events.subscribed).toHaveLength(1); From cf3bb62037595f5e7dfc5270953083f24a96dbc5 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Thu, 29 Aug 2024 10:56:12 -0400 Subject: [PATCH 05/16] Remove deprecated package and fix yarn file Signed-off-by: David Lilienfeld --- .../package.json | 1 - yarn.lock | 24 ------------------- 2 files changed, 25 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index d31a3fc87d..922d6aea43 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -52,7 +52,6 @@ "dependencies": { "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", - "@backstage/backend-tasks": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", diff --git a/yarn.lock b/yarn.lock index dd86d50acc..b3a662a179 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3794,29 +3794,6 @@ __metadata: languageName: unknown linkType: soft -"@backstage/backend-tasks@workspace:^, @backstage/backend-tasks@workspace:packages/backend-tasks": - version: 0.0.0-use.local - resolution: "@backstage/backend-tasks@workspace:packages/backend-tasks" - dependencies: - "@backstage/backend-common": "workspace:^" - "@backstage/backend-plugin-api": "workspace:^" - "@backstage/backend-test-utils": "workspace:^" - "@backstage/cli": "workspace:^" - "@backstage/config": "workspace:^" - "@backstage/errors": "workspace:^" - "@backstage/types": "workspace:^" - "@opentelemetry/api": ^1.3.0 - "@types/luxon": ^3.0.0 - cron: ^3.0.0 - knex: ^3.0.0 - lodash: ^4.17.21 - luxon: ^3.0.0 - uuid: ^9.0.0 - wait-for-expect: ^3.0.2 - zod: ^3.22.4 - languageName: unknown - linkType: soft - "@backstage/backend-test-utils@workspace:^, @backstage/backend-test-utils@workspace:packages/backend-test-utils": version: 0.0.0-use.local resolution: "@backstage/backend-test-utils@workspace:packages/backend-test-utils" @@ -5762,7 +5739,6 @@ __metadata: dependencies: "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" - "@backstage/backend-tasks": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" From cb417ca1113b5097ab83a8cbca40ca07d0400298 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sat, 9 Nov 2024 22:25:34 -0500 Subject: [PATCH 06/16] Fixed all catalog update tests Signed-off-by: David Lilienfeld --- .../package.json | 1 - .../report.api.md | 12 ++++++-- .../src/lib/types.ts | 4 --- ...alogModuleBitbucketServerEntityProvider.ts | 12 +++++++- .../BitbucketServerEntityProvider.test.ts | 30 ++++++++++++------- .../BitbucketServerEntityProvider.ts | 23 +++++++------- ...pi-report-alpha.md => report-alpha.api.md} | 2 +- .../{api-report.md => report.api.md} | 11 +++++++ .../src/index.ts | 2 ++ .../eventsModuleBitbucketServerEventRouter.ts | 2 +- yarn.lock | 1 - 11 files changed, 69 insertions(+), 31 deletions(-) rename plugins/events-backend-module-bitbucket-server/{api-report-alpha.md => report-alpha.api.md} (97%) rename plugins/events-backend-module-bitbucket-server/{api-report.md => report.api.md} (55%) diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 922d6aea43..0d5ba5611e 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -50,7 +50,6 @@ "test": "backstage-cli package test" }, "dependencies": { - "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index 040626a83d..94a0fed689 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -4,6 +4,7 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; +import { AuthService } from '@backstage/backend-plugin-api'; import { BitbucketServerIntegrationConfig } from '@backstage/integration'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; @@ -15,7 +16,6 @@ import { LocationSpec } from '@backstage/plugin-catalog-common'; import { LoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api'; -import { TokenManager } from '@backstage/backend-common'; // @public export class BitbucketServerClient { @@ -55,6 +55,8 @@ export class BitbucketServerClient { }; } +// Warning: (ae-missing-release-tag) "BitbucketServerDefaultBranch" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BitbucketServerDefaultBranch = { id: string; @@ -78,7 +80,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { schedule?: SchedulerServiceTaskRunner; scheduler?: SchedulerService; catalogApi?: CatalogApi; - tokenManager?: TokenManager; + auth?: AuthService; }, ): BitbucketServerEntityProvider[]; getProviderName(): string; @@ -86,6 +88,8 @@ export class BitbucketServerEntityProvider implements EntityProvider { refresh(logger: LoggerService): Promise; } +// Warning: (ae-missing-release-tag) "BitbucketServerEvents" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export namespace BitbucketServerEvents { // (undocumented) @@ -156,11 +160,15 @@ export type BitbucketServerPagedResponse = { nextPageStart: number; }; +// Warning: (ae-missing-release-tag) "BitbucketServerProject" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BitbucketServerProject = { key: string; }; +// Warning: (ae-missing-release-tag) "BitbucketServerRepository" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// // @public (undocumented) export type BitbucketServerRepository = { project: { diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts index e014528791..51e014fb97 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -/** @public */ type BitbucketServerRepository = { project: { key: string; @@ -31,12 +30,10 @@ type BitbucketServerRepository = { defaultBranch: string; }; -/** @public */ type BitbucketServerProject = { key: string; }; -/** @public */ type BitbucketServerDefaultBranch = { id: string; displayId: string; @@ -46,7 +43,6 @@ type BitbucketServerDefaultBranch = { isDefault: boolean; }; -/** @public */ namespace BitbucketServerEvents { interface Event { eventKey: string; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts index a829428296..2d68b6ef49 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts @@ -40,13 +40,23 @@ export const catalogModuleBitbucketServerEntityProvider = createBackendModule({ events: eventsServiceRef, logger: coreServices.logger, scheduler: coreServices.scheduler, + auth: coreServices.auth, }, - async init({ catalog, catalogApi, config, events, logger, scheduler }) { + async init({ + catalog, + catalogApi, + config, + events, + logger, + scheduler, + auth, + }) { const providers = BitbucketServerEntityProvider.fromConfig(config, { catalogApi, events, logger, scheduler, + auth, }); catalog.addEntityProvider(providers); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts index dd2735177b..81699550bd 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { TokenManager } from '@backstage/backend-common'; import { SchedulerService, SchedulerServiceTaskRunner, SchedulerServiceTaskInvocationDefinition, + AuthService, } from '@backstage/backend-plugin-api'; import { mockServices, @@ -69,6 +69,8 @@ function pagedResponse(values: any): BitbucketServerPagedResponse { const logger = mockServices.logger.mock(); +const authService = mockServices.auth.mock(); + const events = DefaultEventsService.create({ logger }); const server = setupServer(); @@ -161,11 +163,11 @@ function setupRepositoryReqHandler(defaultBranch: string) { ); } -const tokenManager = { - getToken: async () => { - return { token: 'fake-token' }; - }, -} as any as TokenManager; +// const authService = { +// getPluginRequestToken: async ({onBehalfOf, targetPluginId}) => { +// return { token: 'fake-token' }; +// }, +// } as any as AuthService; const repoPushEvent: BitbucketServerEvents.RefsChangedEvent = { eventKey: 'repo:refs_changed', date: '2017-09-19T09:45:32+1000', @@ -769,6 +771,10 @@ describe('BitbucketServerEntityProvider', () => { setupRepositoryReqHandler('master'); + authService.getPluginRequestToken.mockResolvedValue({ + token: 'fake-token', + }); + const config = new ConfigReader({ integrations: { bitbucketServer: [ @@ -809,12 +815,13 @@ describe('BitbucketServerEntityProvider', () => { }; }, }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { catalogApi: catalogApi as any as CatalogApi, logger, schedule, events, - tokenManager, + auth: authService as any as AuthService, })[0]; await provider.connect(entityProviderConnection); @@ -885,7 +892,7 @@ describe('BitbucketServerEntityProvider', () => { logger, schedule, events, - tokenManager, + auth: authService as any as AuthService, })[0]; await provider.connect(entityProviderConnection); @@ -898,6 +905,9 @@ describe('BitbucketServerEntityProvider', () => { it('add onRepoPush', async () => { const schedule = new PersistingTaskRunner(); setupRepositoryReqHandler('master'); + authService.getPluginRequestToken.mockResolvedValue({ + token: 'fake-token', + }); const addedModule = createLocationEntity( test1RepoUrl, `/added-module:${targetPath}`, @@ -945,7 +955,7 @@ describe('BitbucketServerEntityProvider', () => { logger, schedule, events, - tokenManager, + auth: authService as any as AuthService, })[0]; await provider.connect(entityProviderConnection); @@ -1015,7 +1025,7 @@ describe('BitbucketServerEntityProvider', () => { logger, schedule, events, - tokenManager, + auth: authService as any as AuthService, })[0]; await provider.connect(entityProviderConnection); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index aa7c3d5863..85878468cc 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -37,6 +37,7 @@ import { defaultBitbucketServerLocationParser, } from './BitbucketServerLocationParser'; import { + AuthService, LoggerService, SchedulerService, SchedulerServiceTaskRunner, @@ -44,7 +45,6 @@ import { import { BitbucketServerEvents } from '../lib'; import { EventsService } from '@backstage/plugin-events-node'; import { CatalogApi } from '@backstage/catalog-client'; -import { TokenManager } from '@backstage/backend-common'; const TOPIC_REPO_REFS_CHANGED = 'bitbucketServer.repo:refs_changed'; @@ -65,7 +65,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { private connection?: EntityProviderConnection; private readonly catalogApi?: CatalogApi; private readonly events?: EventsService; - private readonly tokenManager?: TokenManager; + private readonly auth?: AuthService; private eventConfigErrorThrown = false; private readonly targetAnnotation: string; private readonly defaultBranchAnnotation: string; @@ -79,7 +79,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { schedule?: SchedulerServiceTaskRunner; scheduler?: SchedulerService; catalogApi?: CatalogApi; - tokenManager?: TokenManager; + auth?: AuthService; }, ): BitbucketServerEntityProvider[] { const integrations = ScmIntegrations.fromConfig(config); @@ -116,7 +116,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { options.parser, options.catalogApi, options.events, - options.tokenManager, + options.auth, ); }); } @@ -129,7 +129,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { parser?: BitbucketServerLocationParser, catalogApi?: CatalogApi, events?: EventsService, - tokenManager?: TokenManager, + auth?: AuthService, ) { this.integration = integration; this.config = config; @@ -139,7 +139,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { }); this.scheduleFn = this.createScheduleFn(taskRunner); this.catalogApi = catalogApi; - this.tokenManager = tokenManager; + this.auth = auth; this.targetAnnotation = `${this.config.host.split(':')[0]}/repo-url`; this.defaultBranchAnnotation = 'bitbucket.org/default-branch'; this.events = events; @@ -301,8 +301,8 @@ export class BitbucketServerEntityProvider implements EntityProvider { if ( this.catalogApi !== undefined && this.catalogApi !== null && - this.tokenManager !== undefined && - this.tokenManager !== null + this.auth !== undefined && + this.auth !== null ) { return true; } @@ -451,7 +451,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { ): Promise { if (!this.canHandleEvents()) { this.logger.error( - 'Bitbucket Server catalog entity provider is not set up to handle events. Missing tokenManager or catalogApi.', + 'Bitbucket Server catalog entity provider is not set up to handle events. Missing authService or catalogApi.', ); return; } @@ -468,7 +468,10 @@ export class BitbucketServerEntityProvider implements EntityProvider { this.logger.error('Failed to create location entity.'); return; } - const { token } = await this.tokenManager!.getToken(); + const { token } = await this.auth!.getPluginRequestToken({ + onBehalfOf: await this.auth!.getOwnServiceCredentials(), + targetPluginId: 'catalog', // e.g. 'catalog' + }); const existing = await this.findExistingLocations(catalogRepoUrl, token); const stillExisting: LocationEntity[] = []; const removed: DeferredEntity[] = []; diff --git a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md b/plugins/events-backend-module-bitbucket-server/report-alpha.api.md similarity index 97% rename from plugins/events-backend-module-bitbucket-server/api-report-alpha.md rename to plugins/events-backend-module-bitbucket-server/report-alpha.api.md index 3c4ce45a1f..61559711f3 100644 --- a/plugins/events-backend-module-bitbucket-server/api-report-alpha.md +++ b/plugins/events-backend-module-bitbucket-server/report-alpha.api.md @@ -5,7 +5,7 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -// @alpha +// @public const eventsModuleBitbucketServerEventRouter: BackendFeature; export default eventsModuleBitbucketServerEventRouter; export { eventsModuleBitbucketServerEventRouter }; diff --git a/plugins/events-backend-module-bitbucket-server/api-report.md b/plugins/events-backend-module-bitbucket-server/report.api.md similarity index 55% rename from plugins/events-backend-module-bitbucket-server/api-report.md rename to plugins/events-backend-module-bitbucket-server/report.api.md index 18292a0d11..aaf7b22293 100644 --- a/plugins/events-backend-module-bitbucket-server/api-report.md +++ b/plugins/events-backend-module-bitbucket-server/report.api.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { EventParams } from '@backstage/plugin-events-node'; import { EventsService } from '@backstage/plugin-events-node'; import { SubTopicEventRouter } from '@backstage/plugin-events-node'; @@ -15,4 +16,14 @@ export class BitbucketServerEventRouter extends SubTopicEventRouter { // (undocumented) protected getSubscriberId(): string; } + +// @public +const eventsModuleBitbucketServerEventRouter: BackendFeature; +export default eventsModuleBitbucketServerEventRouter; +export { eventsModuleBitbucketServerEventRouter }; + +// Warnings were encountered during analysis: +// +// src/router/BitbucketServerEventRouter.d.ts:13:5 - (ae-undocumented) Missing documentation for "getSubscriberId". +// src/router/BitbucketServerEventRouter.d.ts:14:5 - (ae-undocumented) Missing documentation for "determineSubTopic". ``` diff --git a/plugins/events-backend-module-bitbucket-server/src/index.ts b/plugins/events-backend-module-bitbucket-server/src/index.ts index 78c97808a4..afa3e204d2 100644 --- a/plugins/events-backend-module-bitbucket-server/src/index.ts +++ b/plugins/events-backend-module-bitbucket-server/src/index.ts @@ -22,3 +22,5 @@ */ export { BitbucketServerEventRouter } from './router/BitbucketServerEventRouter'; +export { eventsModuleBitbucketServerEventRouter } from './service/eventsModuleBitbucketServerEventRouter'; +export { eventsModuleBitbucketServerEventRouter as default } from './service/eventsModuleBitbucketServerEventRouter'; diff --git a/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts index 60fc67eb4f..2a8a26358b 100644 --- a/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts +++ b/plugins/events-backend-module-bitbucket-server/src/service/eventsModuleBitbucketServerEventRouter.ts @@ -23,7 +23,7 @@ import { BitbucketServerEventRouter } from '../router/BitbucketServerEventRouter * * Registers the `BitbucketServerEventRouter`. * - * @alpha + * @public */ export const eventsModuleBitbucketServerEventRouter = createBackendModule({ pluginId: 'events', diff --git a/yarn.lock b/yarn.lock index b3a662a179..baddc683e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5737,7 +5737,6 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend-module-bitbucket-server@workspace:plugins/catalog-backend-module-bitbucket-server" dependencies: - "@backstage/backend-common": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-client": "workspace:^" From 6d489aaac5eb6dfb783c49020c2aa46c4ae050b9 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sun, 10 Nov 2024 20:27:30 -0500 Subject: [PATCH 07/16] Remove documentation temporarily Signed-off-by: David Lilienfeld --- .../report.api.md | 17 +-- .../src/lib/types.ts | 4 + .../BitbucketServerEntityProvider.ts | 125 ------------------ 3 files changed, 13 insertions(+), 133 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index 94a0fed689..abf7c913c8 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -5,6 +5,7 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; import { AuthService } from '@backstage/backend-plugin-api'; +import { BackendFeature } from '@backstage/backend-plugin-api'; import { BitbucketServerIntegrationConfig } from '@backstage/integration'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; @@ -55,8 +56,6 @@ export class BitbucketServerClient { }; } -// Warning: (ae-missing-release-tag) "BitbucketServerDefaultBranch" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type BitbucketServerDefaultBranch = { id: string; @@ -88,8 +87,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { refresh(logger: LoggerService): Promise; } -// Warning: (ae-missing-release-tag) "BitbucketServerEvents" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export namespace BitbucketServerEvents { // (undocumented) @@ -160,15 +157,11 @@ export type BitbucketServerPagedResponse = { nextPageStart: number; }; -// Warning: (ae-missing-release-tag) "BitbucketServerProject" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type BitbucketServerProject = { key: string; }; -// Warning: (ae-missing-release-tag) "BitbucketServerRepository" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type BitbucketServerRepository = { project: { @@ -189,4 +182,12 @@ export type BitbucketServerRepository = { // @public (undocumented) const catalogModuleBitbucketServerEntityProvider: BackendFeature; export default catalogModuleBitbucketServerEntityProvider; + +// @public (undocumented) +export function paginated( + request: ( + options: BitbucketServerListOptions, + ) => Promise>, + options?: BitbucketServerListOptions, +): AsyncGenerator; ``` diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts index 51e014fb97..e014528791 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +/** @public */ type BitbucketServerRepository = { project: { key: string; @@ -30,10 +31,12 @@ type BitbucketServerRepository = { defaultBranch: string; }; +/** @public */ type BitbucketServerProject = { key: string; }; +/** @public */ type BitbucketServerDefaultBranch = { id: string; displayId: string; @@ -43,6 +46,7 @@ type BitbucketServerDefaultBranch = { isDefault: boolean; }; +/** @public */ namespace BitbucketServerEvents { interface Event { eventKey: string; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index 85878468cc..c6df6a19b4 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -282,10 +282,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { return result; } - /** - * Checks if the webhook was triggered on a commit to the head branch of a repository - * @param event Bitbucket Server webhook repo:refs_changed event - */ private isDefaultBranchPush( defaultBranch: String, event: BitbucketServerEvents.RefsChangedEvent, @@ -317,78 +313,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { return false; } - /** - * Creates a Bitbucket Server location entity for the repository that is referenced in the push event passed in. - * - * @param event A Bitbucket Server push event with repository information. - * - * @returns An array of location entities for the repository. - * - * @example - * - * const RefsChangedEvent = { - * "eventKey": "repo:refs_changed", - * "date": "2022-01-01T00:00:00Z", - * "actor": { - * "name": "johndoe", - * "emailAddress": "johndoe@example.com", - * "id": 123, - * "displayName": "John Doe", - * "active": true, - * "slug": "johndoe", - * "type": "NORMAL" - * }, - * "repository": { - * "slug": "my-repo", - * "id": 123, - * "name": "My Repository", - * "project": { - * "key": "my-project", - * "id": 456, - * "name": "My Project", - * "description": "My project description", - * "public": true, - * "type": "NORMAL" - * } - * }, - * "changes": [ - * { - * "ref": { - * "id": "refs/heads/master", - * "displayId": "master", - * "type": "BRANCH" - * }, - * "refId": "refs/heads/master", - * "fromHash": "0123456789abcdef0123456789abcdef0123456", - * "toHash": "fedcba9876543210fedcba9876543210fedcba9", - * "type": "UPDATE" - * } - * ] - * }; - * - * const locationEntities = await getLocationEntity(RefsChangedEvent); - * - * // locationEntities: - * // [ - * // { - * // kind: 'Location', - * // metadata: { - * // name: 'my-repo', - * // namespace: 'my-project', - * // annotations: { - * // 'backstage.io/managed-by-location': 'url/catalog-info-path', - * // 'backstage.io/managed-by-origin-location': 'url/catalog-info-path', - * // 'host/repo-url': 'url', - * // }, - * // }, - * // spec: { - * // type: 'bitbucket', - * // target: 'url/catalog-info-path', - * // presence: 'optional', - * // }, - * // }, - * // ] - */ private async getLocationEntity( event: BitbucketServerEvents.RefsChangedEvent, ): Promise { @@ -441,11 +365,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { return result; } - /** - * Finds if there are existing location entities for the repository that was pushed. If there are, it simply refreshes those entities, - * if not, it discovers any entity that was added and removed in the list of entities - * @param event - A Bitbucket Server webhook event for repo:refs_change - */ private async onRepoPush( event: BitbucketServerEvents.RefsChangedEvent, ): Promise { @@ -547,12 +466,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { return; } - /** - * Gets the location entities that are to be newly added to the catalog. - * @param targets Location entities for catalog files in the repository that was pushed - * @param existing The location entities in the repository that was pushed that already exist - * @returns Returns all deferred entities that represent location entities that don't exist in the catalog yet - */ private async getAddedEntities( targets: Entity[], existing: LocationEntity[], @@ -571,12 +484,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { return added; } - /** - * Finds all location entities in the catalog that already have the annotation `metadata.annotations.${this.config.host}/repo-url` - * that is equivalent to @param repoURL\. - * @param repoURL URL for the reposity that the method finds the existing location entities for - * @param token Token from class token manager - */ private async findExistingLocations( catalogRepoUrl: string, token: string, @@ -589,40 +496,8 @@ export class BitbucketServerEntityProvider implements EntityProvider { result => result.items, ) as Promise; } - - // private static toLocationSpec(target: string): LocationSpec { - // return { - // type: 'url', - // target: target, - // presence: 'required', - // }; - // } } -/** - * Converts an array of entities into an array of deferred entities with the provider's name as the location key. - * - * @param targets An array of entities to convert. - * - * @returns An array of deferred entities with the provider's name as the location key. - * - * @example - * - * const entities = [ - * { kind: 'Component', namespace: 'default', name: 'my-component' }, - * { kind: 'System', namespace: 'default', name: 'my-system' }, - * { kind: 'API', namespace: 'default', name: 'my-api' }, - * ]; - * - * const deferredEntities = toDeferredEntities(entities); - * - * // deferredEntities: - * // [ - * // { locationKey: 'my-provider', entity: { kind: 'Component', namespace: 'default', name: 'my-component' } }, - * // { locationKey: 'my-provider', entity: { kind: 'System', namespace: 'default', name: 'my-system' } }, - * // { locationKey: 'my-provider', entity: { kind: 'API', namespace: 'default', name: 'my-api' } }, - * // ] - */ export function toDeferredEntities( targets: Entity[], locationKey: string, From a8a42c387d70362f07c72df45109822aea5d3c6f Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sun, 24 Nov 2024 20:12:20 -0500 Subject: [PATCH 08/16] Fix api report Signed-off-by: David Lilienfeld --- plugins/catalog-backend-module-bitbucket-server/report.api.md | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index abf7c913c8..add82b0a90 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; import { AuthService } from '@backstage/backend-plugin-api'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { BitbucketServerIntegrationConfig } from '@backstage/integration'; From 0f12f17fb21f93664678a2fb026acaa3a83a94ab Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sun, 24 Nov 2024 20:31:44 -0500 Subject: [PATCH 09/16] Fix events backend BBS report Signed-off-by: David Lilienfeld --- plugins/events-backend-module-bitbucket-server/report.api.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/plugins/events-backend-module-bitbucket-server/report.api.md b/plugins/events-backend-module-bitbucket-server/report.api.md index aaf7b22293..e0b55a5f57 100644 --- a/plugins/events-backend-module-bitbucket-server/report.api.md +++ b/plugins/events-backend-module-bitbucket-server/report.api.md @@ -21,9 +21,4 @@ export class BitbucketServerEventRouter extends SubTopicEventRouter { const eventsModuleBitbucketServerEventRouter: BackendFeature; export default eventsModuleBitbucketServerEventRouter; export { eventsModuleBitbucketServerEventRouter }; - -// Warnings were encountered during analysis: -// -// src/router/BitbucketServerEventRouter.d.ts:13:5 - (ae-undocumented) Missing documentation for "getSubscriberId". -// src/router/BitbucketServerEventRouter.d.ts:14:5 - (ae-undocumented) Missing documentation for "determineSubTopic". ``` From 1fa7494869b15531ac4bbbb985c1ffe17820b2a6 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Fri, 6 Dec 2024 23:43:50 -0500 Subject: [PATCH 10/16] Fix yarn lock Signed-off-by: David Lilienfeld --- yarn.lock | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index baddc683e1..554c2410f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5747,9 +5747,13 @@ __metadata: "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" + "@backstage/plugin-events-backend-test-utils": "workspace:^" + "@backstage/plugin-events-node": "workspace:^" + "@types/node-fetch": ^2.5.12 luxon: ^3.0.0 msw: ^1.0.0 p-throttle: ^4.1.1 + node-fetch: ^2.7.0 uuid: ^11.0.0 languageName: unknown linkType: soft @@ -20106,7 +20110,7 @@ __metadata: languageName: node linkType: hard -"@types/node-fetch@npm:^2.6.4, @types/node-fetch@npm:^2.6.9": +"@types/node-fetch@npm:^2.5.12, @types/node-fetch@npm:^2.6.4, @types/node-fetch@npm:^2.6.9": version: 2.6.12 resolution: "@types/node-fetch@npm:2.6.12" dependencies: From 26fe586505050d511e8fcef2ecf80b8bb0b76991 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Fri, 6 Dec 2024 23:58:48 -0500 Subject: [PATCH 11/16] Fix package deps catalog plugin Signed-off-by: David Lilienfeld --- plugins/catalog-backend-module-bitbucket-server/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 0d5ba5611e..0f2861ca56 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -60,7 +60,6 @@ "@backstage/plugin-catalog-node": "workspace:^", "p-throttle": "^4.1.1", "@backstage/plugin-events-node": "workspace:^", - "@types/node-fetch": "^2.5.12", "node-fetch": "^2.7.0", "uuid": "^11.0.0" }, @@ -68,6 +67,7 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", + "@types/node-fetch": "^2.5.12", "luxon": "^3.0.0", "msw": "^1.0.0" }, From 351f3aedb9828e16b1d779e46af969684b98193e Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Fri, 13 Dec 2024 17:39:11 -0500 Subject: [PATCH 12/16] Fix package.json and yarn.lock Signed-off-by: David Lilienfeld --- plugins/catalog-backend-module-bitbucket-server/package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 0f2861ca56..45edfa150f 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -58,9 +58,9 @@ "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", - "p-throttle": "^4.1.1", "@backstage/plugin-events-node": "workspace:^", "node-fetch": "^2.7.0", + "p-throttle": "^4.1.1", "uuid": "^11.0.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index 554c2410f7..4605124ba9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5752,8 +5752,8 @@ __metadata: "@types/node-fetch": ^2.5.12 luxon: ^3.0.0 msw: ^1.0.0 - p-throttle: ^4.1.1 node-fetch: ^2.7.0 + p-throttle: ^4.1.1 uuid: ^11.0.0 languageName: unknown linkType: soft From 00d989fd7a943d7e221585d2dc83b18dd5cb2aa4 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Fri, 13 Dec 2024 17:43:19 -0500 Subject: [PATCH 13/16] Moved throttle below imports in BitbucketServerClient Signed-off-by: David Lilienfeld --- .../src/lib/BitbucketServerClient.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts index ba5f75268c..a29b0ae4ff 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts @@ -19,6 +19,13 @@ import { getBitbucketServerRequestOptions, } from '@backstage/integration'; import pThrottle from 'p-throttle'; +import { + BitbucketServerDefaultBranch, + BitbucketServerRepository, +} from './index'; +import { BitbucketServerProject } from './types'; +import { NotFoundError } from '@backstage/errors'; +import { ResponseError } from '@backstage/errors'; // 1 per second const throttle = pThrottle({ @@ -31,15 +38,6 @@ const throttledFetch = throttle( return await fetch(url, options); }, ); -import { - BitbucketServerDefaultBranch, - BitbucketServerRepository, -} from './index'; - -import { BitbucketServerProject } from './types'; -import { NotFoundError } from '@backstage/errors'; - -import { ResponseError } from '@backstage/errors'; /** * A client for interacting with a Bitbucket Server instance From bb305509fdf3f213e5a512ecc5e147bdd12f7e91 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sun, 2 Mar 2025 22:52:59 -0500 Subject: [PATCH 14/16] Fix all suggestions | working tests Signed-off-by: David Lilienfeld --- .../integrations/bitbucketServer/discovery.md | 10 ++- .../package.json | 2 - .../report-alpha.api.md | 6 -- .../report.api.md | 4 +- .../src/alpha.ts | 6 +- .../src/lib/BitbucketServerClient.ts | 1 + ...alogModuleBitbucketServerEntityProvider.ts | 6 +- .../BitbucketServerEntityProvider.test.ts | 71 +++++++------------ .../BitbucketServerEntityProvider.ts | 31 ++++---- .../README.md | 14 +--- .../package.json | 2 +- .../report-alpha.api.md | 7 -- .../report.api.md | 12 ---- .../src/alpha.ts | 3 +- .../src/index.ts | 1 - yarn.lock | 4 +- 16 files changed, 52 insertions(+), 128 deletions(-) diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md index a66db83806..150e92f11b 100644 --- a/docs/integrations/bitbucketServer/discovery.md +++ b/docs/integrations/bitbucketServer/discovery.md @@ -30,14 +30,12 @@ yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbuck ```ts // optional if you want HTTP endpojnts to receive external events -// backend.add(import('@backstage/plugin-events-backend/alpha')); +// backend.add(import('@backstage/plugin-events-backend')); // optional if you want to use AWS SQS instead of HTTP endpoints to receive external events -// backend.add(import('@backstage/plugin-events-backend-module-aws-sqs/alpha')); +// backend.add(import('@backstage/plugin-events-backend-module-aws-sqs')); +backend.add(import('@backstage/plugin-events-backend-module-bitbucket-server')); backend.add( - import('@backstage/plugin-events-backend-module-bitbucket-server/alpha'), -); -backend.add( - import('@backstage/plugin-catalog-backend-module-bitbucket-server/alpha'), + import('@backstage/plugin-catalog-backend-module-bitbucket-server'), ); ``` diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 45edfa150f..41c4fabd69 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -59,7 +59,6 @@ "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", "@backstage/plugin-events-node": "workspace:^", - "node-fetch": "^2.7.0", "p-throttle": "^4.1.1", "uuid": "^11.0.0" }, @@ -67,7 +66,6 @@ "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@backstage/plugin-events-backend-test-utils": "workspace:^", - "@types/node-fetch": "^2.5.12", "luxon": "^3.0.0", "msw": "^1.0.0" }, diff --git a/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md b/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md index 06212c3c60..7082701333 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md @@ -3,11 +3,5 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; - -// @alpha (undocumented) -const _feature: BackendFeature; -export default _feature; - // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index add82b0a90..cdbe714be3 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -6,7 +6,7 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { BackendFeature } from '@backstage/backend-plugin-api'; import { BitbucketServerIntegrationConfig } from '@backstage/integration'; -import { CatalogApi } from '@backstage/catalog-client'; +import { CatalogService } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; import { EntityProvider } from '@backstage/plugin-catalog-node'; @@ -77,7 +77,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { parser?: BitbucketServerLocationParser; schedule?: SchedulerServiceTaskRunner; scheduler?: SchedulerService; - catalogApi?: CatalogApi; + catalogApi?: CatalogService; auth?: AuthService; }, ): BitbucketServerEntityProvider[]; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts b/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts index cba672ce49..706d03bd5d 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts @@ -14,8 +14,4 @@ * limitations under the License. */ -import { default as feature } from './module'; - -/** @alpha */ -const _feature = feature; -export default _feature; +import {} from './module'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts index a29b0ae4ff..6deb47ca6a 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts @@ -197,6 +197,7 @@ export type BitbucketServerPagedResponse = { values: T[]; nextPageStart: number; }; + /** * @public */ diff --git a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts index 2d68b6ef49..d66ef4e2a2 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/module/catalogModuleBitbucketServerEntityProvider.ts @@ -18,10 +18,8 @@ import { coreServices, createBackendModule, } from '@backstage/backend-plugin-api'; -import { - catalogProcessingExtensionPoint, - catalogServiceRef, -} from '@backstage/plugin-catalog-node/alpha'; +import { catalogServiceRef } from '@backstage/plugin-catalog-node'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { eventsServiceRef } from '@backstage/plugin-events-node'; import { BitbucketServerEntityProvider } from '../providers/BitbucketServerEntityProvider'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts index 81699550bd..ba4e3e0411 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts @@ -18,7 +18,7 @@ import { SchedulerService, SchedulerServiceTaskRunner, SchedulerServiceTaskInvocationDefinition, - AuthService, + BackstageCredentials, } from '@backstage/backend-plugin-api'; import { mockServices, @@ -26,6 +26,7 @@ import { } from '@backstage/backend-test-utils'; import { ConfigReader } from '@backstage/config'; import { + CatalogService, DeferredEntity, EntityProviderConnection, locationSpecToLocationEntity, @@ -39,8 +40,8 @@ import { import { BitbucketServerPagedResponse } from '../lib'; import { Entity, LocationEntity } from '@backstage/catalog-model'; import { BitbucketServerEvents } from '../lib/index'; -import { CatalogApi } from '@backstage/catalog-client'; import { DefaultEventsService } from '@backstage/plugin-events-node'; +import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; class PersistingTaskRunner implements SchedulerServiceTaskRunner { private tasks: SchedulerServiceTaskInvocationDefinition[] = []; @@ -163,11 +164,6 @@ function setupRepositoryReqHandler(defaultBranch: string) { ); } -// const authService = { -// getPluginRequestToken: async ({onBehalfOf, targetPluginId}) => { -// return { token: 'fake-token' }; -// }, -// } as any as AuthService; const repoPushEvent: BitbucketServerEvents.RefsChangedEvent = { eventKey: 'repo:refs_changed', date: '2017-09-19T09:45:32+1000', @@ -771,9 +767,7 @@ describe('BitbucketServerEntityProvider', () => { setupRepositoryReqHandler('master'); - authService.getPluginRequestToken.mockResolvedValue({ - token: 'fake-token', - }); + // authService.getOwnServiceCredentials(); const config = new ConfigReader({ integrations: { @@ -797,13 +791,14 @@ describe('BitbucketServerEntityProvider', () => { }, }); - const catalogApi = { + const catalogApi = catalogServiceMock.mock({ getEntities: async ( request: { filter: Record }, - options: { token: string }, + credentials: { credentials: BackstageCredentials }, ): Promise<{ items: Entity[] }> => { if ( - options.token !== 'fake-token' || + credentials.credentials !== + (await authService.getOwnServiceCredentials()) || request.filter.kind !== 'Location' || request.filter[`metadata.annotations.${host}/repo-url`] !== `${test1RepoUrl}/kept-module:${targetPath}` @@ -814,14 +809,14 @@ describe('BitbucketServerEntityProvider', () => { items: [keptModule], }; }, - }; + }); const provider = BitbucketServerEntityProvider.fromConfig(config, { - catalogApi: catalogApi as any as CatalogApi, + catalogApi: catalogApi, logger, schedule, events, - auth: authService as any as AuthService, + auth: authService, })[0]; await provider.connect(entityProviderConnection); @@ -869,13 +864,14 @@ describe('BitbucketServerEntityProvider', () => { }, }); - const catalogApi = { + const catalogApi = catalogServiceMock.mock({ getEntities: async ( request: { filter: Record }, - options: { token: string }, + credentials: { credentials: BackstageCredentials }, ): Promise<{ items: Entity[] }> => { if ( - options.token !== 'fake-token' || + credentials.credentials !== + (await authService.getOwnServiceCredentials()) || request.filter.kind !== 'Location' || request.filter[`metadata.annotations.${host}/repo-url`] !== `${test1RepoUrl}/kept-module:${targetPath}` @@ -886,13 +882,14 @@ describe('BitbucketServerEntityProvider', () => { items: [keptModule], }; }, - }; + }); + const provider = BitbucketServerEntityProvider.fromConfig(config, { - catalogApi: catalogApi as any as CatalogApi, + catalogApi: catalogApi, logger, schedule, events, - auth: authService as any as AuthService, + auth: authService, })[0]; await provider.connect(entityProviderConnection); @@ -940,22 +937,13 @@ describe('BitbucketServerEntityProvider', () => { }, }); - const catalogApi = { - getEntities: async ( - _request: { filter: Record }, - _options: { token: string }, - ): Promise<{ items: Entity[] }> => { - return { - items: [], - }; - }, - }; + const catalogApi = catalogServiceMock({ entities: [] }); const provider = BitbucketServerEntityProvider.fromConfig(config, { - catalogApi: catalogApi as any as CatalogApi, + catalogApi: catalogApi, logger, schedule, events, - auth: authService as any as AuthService, + auth: authService, })[0]; await provider.connect(entityProviderConnection); @@ -1010,22 +998,13 @@ describe('BitbucketServerEntityProvider', () => { }, }); - const catalogApi = { - getEntities: async ( - _request: { filter: Record }, - _options: { token: string }, - ): Promise<{ items: Entity[] }> => { - return { - items: [], - }; - }, - }; + const catalogApi = catalogServiceMock({ entities: [] }); const provider = BitbucketServerEntityProvider.fromConfig(config, { - catalogApi: catalogApi as any as CatalogApi, + catalogApi: catalogApi as any as CatalogService, logger, schedule, events, - auth: authService as any as AuthService, + auth: authService, })[0]; await provider.connect(entityProviderConnection); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index c6df6a19b4..b462f8756a 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -25,6 +25,7 @@ import { EntityProvider, EntityProviderConnection, DeferredEntity, + CatalogService, } from '@backstage/plugin-catalog-node'; import * as uuid from 'uuid'; import { BitbucketServerClient, paginated } from '../lib'; @@ -38,13 +39,13 @@ import { } from './BitbucketServerLocationParser'; import { AuthService, + BackstageCredentials, LoggerService, SchedulerService, SchedulerServiceTaskRunner, } from '@backstage/backend-plugin-api'; import { BitbucketServerEvents } from '../lib'; import { EventsService } from '@backstage/plugin-events-node'; -import { CatalogApi } from '@backstage/catalog-client'; const TOPIC_REPO_REFS_CHANGED = 'bitbucketServer.repo:refs_changed'; @@ -63,7 +64,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { private readonly logger: LoggerService; private readonly scheduleFn: () => Promise; private connection?: EntityProviderConnection; - private readonly catalogApi?: CatalogApi; + private readonly catalogApi?: CatalogService; private readonly events?: EventsService; private readonly auth?: AuthService; private eventConfigErrorThrown = false; @@ -78,7 +79,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { parser?: BitbucketServerLocationParser; schedule?: SchedulerServiceTaskRunner; scheduler?: SchedulerService; - catalogApi?: CatalogApi; + catalogApi?: CatalogService; auth?: AuthService; }, ): BitbucketServerEntityProvider[] { @@ -127,7 +128,7 @@ export class BitbucketServerEntityProvider implements EntityProvider { logger: LoggerService, taskRunner: SchedulerServiceTaskRunner, parser?: BitbucketServerLocationParser, - catalogApi?: CatalogApi, + catalogApi?: CatalogService, events?: EventsService, auth?: AuthService, ) { @@ -294,19 +295,14 @@ export class BitbucketServerEntityProvider implements EntityProvider { * @returns Boolean */ private canHandleEvents(): boolean { - if ( - this.catalogApi !== undefined && - this.catalogApi !== null && - this.auth !== undefined && - this.auth !== null - ) { + if (this.catalogApi && this.auth) { return true; } if (!this.eventConfigErrorThrown) { this.eventConfigErrorThrown = true; throw new Error( - `${this.getProviderName()} not well configured to handle repo:push. Missing CatalogApi and/or TokenManager.`, + `${this.getProviderName()} not well configured to handle repo:push. Missing CatalogApi and/or AuthService.`, ); } @@ -387,11 +383,10 @@ export class BitbucketServerEntityProvider implements EntityProvider { this.logger.error('Failed to create location entity.'); return; } - const { token } = await this.auth!.getPluginRequestToken({ - onBehalfOf: await this.auth!.getOwnServiceCredentials(), - targetPluginId: 'catalog', // e.g. 'catalog' - }); - const existing = await this.findExistingLocations(catalogRepoUrl, token); + const existing = await this.findExistingLocations( + catalogRepoUrl, + await this.auth!.getOwnServiceCredentials(), + ); const stillExisting: LocationEntity[] = []; const removed: DeferredEntity[] = []; existing.forEach(item => { @@ -486,13 +481,13 @@ export class BitbucketServerEntityProvider implements EntityProvider { private async findExistingLocations( catalogRepoUrl: string, - token: string, + credentials: BackstageCredentials, ): Promise { const filter: Record = {}; filter.kind = 'Location'; filter[`metadata.annotations.${this.targetAnnotation}`] = catalogRepoUrl; - return this.catalogApi!.getEntities({ filter }, { token }).then( + return this.catalogApi!.getEntities({ filter }, { credentials }).then( result => result.items, ) as Promise; } diff --git a/plugins/events-backend-module-bitbucket-server/README.md b/plugins/events-backend-module-bitbucket-server/README.md index 2c8fdff092..4950015010 100644 --- a/plugins/events-backend-module-bitbucket-server/README.md +++ b/plugins/events-backend-module-bitbucket-server/README.md @@ -32,17 +32,5 @@ yarn add --cwd packages/backend @backstage/plugin-events-backend-module-bitbucke ```ts // packages/backend/src/index.ts -backend.add( - import('@backstage/plugin-events-backend-module-bitbucket-server/alpha'), -); -``` - -### Legacy Backend System - -```ts -// packages/backend/src/plugins/events.ts -const eventRouter = new BitbucketCloudEventRouter({ - events: env.events, -}); -await eventRouter.subscribe(); +backend.add(import('@backstage/plugin-events-backend-module-bitbucket-server')); ``` diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json index 4f38cb1f05..4cfe424618 100644 --- a/plugins/events-backend-module-bitbucket-server/package.json +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-server", - "version": "0.1.0", + "version": "0.0.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-server/report-alpha.api.md b/plugins/events-backend-module-bitbucket-server/report-alpha.api.md index 61559711f3..47b9068604 100644 --- a/plugins/events-backend-module-bitbucket-server/report-alpha.api.md +++ b/plugins/events-backend-module-bitbucket-server/report-alpha.api.md @@ -3,12 +3,5 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; - -// @public -const eventsModuleBitbucketServerEventRouter: BackendFeature; -export default eventsModuleBitbucketServerEventRouter; -export { eventsModuleBitbucketServerEventRouter }; - // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/events-backend-module-bitbucket-server/report.api.md b/plugins/events-backend-module-bitbucket-server/report.api.md index e0b55a5f57..916f7fa2ab 100644 --- a/plugins/events-backend-module-bitbucket-server/report.api.md +++ b/plugins/events-backend-module-bitbucket-server/report.api.md @@ -4,18 +4,6 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; -import { EventParams } from '@backstage/plugin-events-node'; -import { EventsService } from '@backstage/plugin-events-node'; -import { SubTopicEventRouter } from '@backstage/plugin-events-node'; - -// @public -export class BitbucketServerEventRouter extends SubTopicEventRouter { - constructor(options: { events: EventsService }); - // (undocumented) - protected determineSubTopic(params: EventParams): string | undefined; - // (undocumented) - protected getSubscriberId(): string; -} // @public const eventsModuleBitbucketServerEventRouter: BackendFeature; diff --git a/plugins/events-backend-module-bitbucket-server/src/alpha.ts b/plugins/events-backend-module-bitbucket-server/src/alpha.ts index c741a37f24..588ee62daa 100644 --- a/plugins/events-backend-module-bitbucket-server/src/alpha.ts +++ b/plugins/events-backend-module-bitbucket-server/src/alpha.ts @@ -14,5 +14,4 @@ * limitations under the License. */ -export { eventsModuleBitbucketServerEventRouter } from './service/eventsModuleBitbucketServerEventRouter'; -export { eventsModuleBitbucketServerEventRouter as default } from './service/eventsModuleBitbucketServerEventRouter'; +import {} from './service/eventsModuleBitbucketServerEventRouter'; diff --git a/plugins/events-backend-module-bitbucket-server/src/index.ts b/plugins/events-backend-module-bitbucket-server/src/index.ts index afa3e204d2..6cf9aeb5e2 100644 --- a/plugins/events-backend-module-bitbucket-server/src/index.ts +++ b/plugins/events-backend-module-bitbucket-server/src/index.ts @@ -21,6 +21,5 @@ * @packageDocumentation */ -export { BitbucketServerEventRouter } from './router/BitbucketServerEventRouter'; export { eventsModuleBitbucketServerEventRouter } from './service/eventsModuleBitbucketServerEventRouter'; export { eventsModuleBitbucketServerEventRouter as default } from './service/eventsModuleBitbucketServerEventRouter'; diff --git a/yarn.lock b/yarn.lock index 4605124ba9..783947ce26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5749,10 +5749,8 @@ __metadata: "@backstage/plugin-catalog-node": "workspace:^" "@backstage/plugin-events-backend-test-utils": "workspace:^" "@backstage/plugin-events-node": "workspace:^" - "@types/node-fetch": ^2.5.12 luxon: ^3.0.0 msw: ^1.0.0 - node-fetch: ^2.7.0 p-throttle: ^4.1.1 uuid: ^11.0.0 languageName: unknown @@ -20110,7 +20108,7 @@ __metadata: languageName: node linkType: hard -"@types/node-fetch@npm:^2.5.12, @types/node-fetch@npm:^2.6.4, @types/node-fetch@npm:^2.6.9": +"@types/node-fetch@npm:^2.6.4, @types/node-fetch@npm:^2.6.9": version: 2.6.12 resolution: "@types/node-fetch@npm:2.6.12" dependencies: From 9785da55cf95508c503e7af259008340ab3e7a29 Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Wed, 5 Mar 2025 12:50:48 -0500 Subject: [PATCH 15/16] Fix api reports Signed-off-by: David Lilienfeld --- .../package.json | 4 -- .../report-alpha.api.md | 7 --- .../report.api.md | 54 ------------------- .../src/alpha.ts | 17 ------ .../src/index.ts | 11 +++- .../src/lib/BitbucketServerClient.ts | 3 -- .../src/lib/index.ts | 8 ++- .../src/lib/types.ts | 1 - .../package.json | 4 -- .../report-alpha.api.md | 7 --- .../report.api.md | 1 - .../src/alpha.ts | 17 ------ .../src/index.ts | 2 - 13 files changed, 17 insertions(+), 119 deletions(-) delete mode 100644 plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md delete mode 100644 plugins/catalog-backend-module-bitbucket-server/src/alpha.ts delete mode 100644 plugins/events-backend-module-bitbucket-server/report-alpha.api.md delete mode 100644 plugins/events-backend-module-bitbucket-server/src/alpha.ts diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index 41c4fabd69..1f9b8baa27 100644 --- a/plugins/catalog-backend-module-bitbucket-server/package.json +++ b/plugins/catalog-backend-module-bitbucket-server/package.json @@ -21,16 +21,12 @@ "license": "Apache-2.0", "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "main": "src/index.ts", "types": "src/index.ts", "typesVersions": { "*": { - "alpha": [ - "src/alpha.ts" - ], "package.json": [ "package.json" ] diff --git a/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md b/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md deleted file mode 100644 index 7082701333..0000000000 --- a/plugins/catalog-backend-module-bitbucket-server/report-alpha.api.md +++ /dev/null @@ -1,7 +0,0 @@ -## API Report File for "@backstage/plugin-catalog-backend-module-bitbucket-server" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -// (No @packageDocumentation comment for this package) -``` diff --git a/plugins/catalog-backend-module-bitbucket-server/report.api.md b/plugins/catalog-backend-module-bitbucket-server/report.api.md index cdbe714be3..7cb13e651f 100644 --- a/plugins/catalog-backend-module-bitbucket-server/report.api.md +++ b/plugins/catalog-backend-module-bitbucket-server/report.api.md @@ -86,52 +86,6 @@ export class BitbucketServerEntityProvider implements EntityProvider { refresh(logger: LoggerService): Promise; } -// @public (undocumented) -export namespace BitbucketServerEvents { - // (undocumented) - export type Actor = { - name?: string; - id: number; - }; - // (undocumented) - export type Change = { - ref: { - id: string; - displayId: string; - type: string; - }; - }; - // (undocumented) - export interface Event { - // (undocumented) - eventKey: string; - } - // (undocumented) - export interface RefsChangedEvent extends Event { - // (undocumented) - actor: Actor; - // (undocumented) - changes: Change[]; - // (undocumented) - commits: undefined; - // (undocumented) - date: string; - // (undocumented) - repository: Repository; - // (undocumented) - ToCommit: undefined; - } - // (undocumented) - export type Repository = { - slug: string; - id: number; - name: string; - project: BitbucketServerProject; - }; - { - } -} - // @public (undocumented) export type BitbucketServerListOptions = { [key: string]: number | undefined; @@ -181,12 +135,4 @@ export type BitbucketServerRepository = { // @public (undocumented) const catalogModuleBitbucketServerEntityProvider: BackendFeature; export default catalogModuleBitbucketServerEntityProvider; - -// @public (undocumented) -export function paginated( - request: ( - options: BitbucketServerListOptions, - ) => Promise>, - options?: BitbucketServerListOptions, -): AsyncGenerator; ``` diff --git a/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts b/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts deleted file mode 100644 index 706d03bd5d..0000000000 --- a/plugins/catalog-backend-module-bitbucket-server/src/alpha.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2023 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. - */ - -import {} from './module'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/index.ts b/plugins/catalog-backend-module-bitbucket-server/src/index.ts index 96e3a1a0a7..722be03d7a 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/index.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/index.ts @@ -21,7 +21,16 @@ */ export { default } from './module'; -export * from './lib'; + +export { BitbucketServerClient } from './lib'; + +export type { + BitbucketServerProject, + BitbucketServerRepository, + BitbucketServerPagedResponse, + BitbucketServerListOptions, + BitbucketServerDefaultBranch, +} from './lib'; export { BitbucketServerEntityProvider } from './providers'; export type { BitbucketServerLocationParser } from './providers'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts index 6deb47ca6a..70e791c992 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts @@ -198,9 +198,6 @@ export type BitbucketServerPagedResponse = { nextPageStart: number; }; -/** - * @public - */ export async function* paginated( request: ( options: BitbucketServerListOptions, diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts index 14fb01629e..4533cb1305 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/index.ts @@ -19,4 +19,10 @@ export type { BitbucketServerPagedResponse, BitbucketServerListOptions, } from './BitbucketServerClient'; -export * from './types'; +export type { + BitbucketServerDefaultBranch, + BitbucketServerRepository, + BitbucketServerEvents, + BitbucketServerProject, +} from './types'; +// export * from './types'; diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts index e014528791..efdf3d7cf8 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/types.ts @@ -46,7 +46,6 @@ type BitbucketServerDefaultBranch = { isDefault: boolean; }; -/** @public */ namespace BitbucketServerEvents { interface Event { eventKey: string; diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json index 4cfe424618..29945bd16f 100644 --- a/plugins/events-backend-module-bitbucket-server/package.json +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -18,16 +18,12 @@ "license": "Apache-2.0", "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "main": "src/index.ts", "types": "src/index.ts", "typesVersions": { "*": { - "alpha": [ - "src/alpha.ts" - ], "package.json": [ "package.json" ] diff --git a/plugins/events-backend-module-bitbucket-server/report-alpha.api.md b/plugins/events-backend-module-bitbucket-server/report-alpha.api.md deleted file mode 100644 index 47b9068604..0000000000 --- a/plugins/events-backend-module-bitbucket-server/report-alpha.api.md +++ /dev/null @@ -1,7 +0,0 @@ -## API Report File for "@backstage/plugin-events-backend-module-bitbucket-server" - -> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). - -```ts -// (No @packageDocumentation comment for this package) -``` diff --git a/plugins/events-backend-module-bitbucket-server/report.api.md b/plugins/events-backend-module-bitbucket-server/report.api.md index 916f7fa2ab..913556cfe9 100644 --- a/plugins/events-backend-module-bitbucket-server/report.api.md +++ b/plugins/events-backend-module-bitbucket-server/report.api.md @@ -8,5 +8,4 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; // @public const eventsModuleBitbucketServerEventRouter: BackendFeature; export default eventsModuleBitbucketServerEventRouter; -export { eventsModuleBitbucketServerEventRouter }; ``` diff --git a/plugins/events-backend-module-bitbucket-server/src/alpha.ts b/plugins/events-backend-module-bitbucket-server/src/alpha.ts deleted file mode 100644 index 588ee62daa..0000000000 --- a/plugins/events-backend-module-bitbucket-server/src/alpha.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2023 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. - */ - -import {} from './service/eventsModuleBitbucketServerEventRouter'; diff --git a/plugins/events-backend-module-bitbucket-server/src/index.ts b/plugins/events-backend-module-bitbucket-server/src/index.ts index 6cf9aeb5e2..8a765ecfdf 100644 --- a/plugins/events-backend-module-bitbucket-server/src/index.ts +++ b/plugins/events-backend-module-bitbucket-server/src/index.ts @@ -20,6 +20,4 @@ * * @packageDocumentation */ - -export { eventsModuleBitbucketServerEventRouter } from './service/eventsModuleBitbucketServerEventRouter'; export { eventsModuleBitbucketServerEventRouter as default } from './service/eventsModuleBitbucketServerEventRouter'; From 79c541d889c53ef1830cd6dcbcd3490ce1f10aca Mon Sep 17 00:00:00 2001 From: David Lilienfeld Date: Sun, 9 Mar 2025 00:02:39 -0500 Subject: [PATCH 16/16] Remove legacy backend docs Signed-off-by: David Lilienfeld --- .../integrations/bitbucketServer/discovery.md | 79 ------------------- 1 file changed, 79 deletions(-) diff --git a/docs/integrations/bitbucketServer/discovery.md b/docs/integrations/bitbucketServer/discovery.md index 150e92f11b..7947913182 100644 --- a/docs/integrations/bitbucketServer/discovery.md +++ b/docs/integrations/bitbucketServer/discovery.md @@ -50,85 +50,6 @@ Further documentation: - - -### Installation with Legacy Backend System - -#### Installation without Events Support - -You will have to add the entity provider in the catalog initialization code of your -backend. The provider is not installed by default, therefore you have to add a -dependency to `@backstage/plugin-catalog-backend-module-bitbucket-server` to your backend -package. - -```bash -# From your Backstage root directory -yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-server -``` - -And then add the entity provider to your catalog builder: - -```ts title="packages/backend/src/index.ts" -backend.add(import('@backstage/plugin-catalog-backend')); -/* highlight-add-start */ -backend.add( - import('@backstage/plugin-catalog-backend-module-bitbucket-server'), -); -/* highlight-add-end */ -``` - -#### Installation with Events Support - -Please follow the installation instructions at - -- -- - -Additionally, you need to decide how you want to receive events from external sources like - -- [via HTTP endpoint](https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md) - - Bitbucket Server events webhook url should be set to `{backstageBaseUrl}/api/events/http/bitbucketServer` -- [via an AWS SQS queue](https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-aws-sqs/README.md) - -Set up your provider - -```ts title="packages/backend/src/plugins/catalog.ts" -import { CatalogBuilder } from '@backstage/plugin-catalog-backend'; -/* highlight-add-start */ -import { BitbucketServerEntityProvider } from '@backstage/plugin-catalog-backend-module-bitbucket-server'; -/* highlight-add-end */ - -import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend'; -import { Router } from 'express'; -import { PluginEnvironment } from '../types'; - -export default async function createPlugin( - env: PluginEnvironment, -): Promise { - const builder = await CatalogBuilder.create(env); - builder.addProcessor(new ScaffolderEntitiesProcessor()); - /* highlight-add-start */ - const bitbucketServerProvider = BitbucketServerEntityProvider.fromConfig( - env.config, - { - catalogApi: new CatalogClient({ discoveryApi: env.discovery }), - logger: env.logger, - scheduler: env.scheduler, - events: env.events, - tokenManager: env.tokenManager, - }, - ); - env.eventBroker.subscribe(bitbucketServerProvider); - builder.addEntityProvider(bitbucketServerProvider); - /* highlight-add-end */ - const { processingEngine, router } = await builder.build(); - await processingEngine.start(); - return router; -} -``` - -**Attention:** -`catalogApi` and `tokenManager` are required at this variant -compared to the one without events support. - ## Configuration To use the entity provider, you'll need a [Bitbucket Server integration set up](locations.md).