Skip to content

Commit

Permalink
fix(events,catalog,bitbucket-cloud)!: fix new backend system support;…
Browse files Browse the repository at this point in the history
… migrates to `EventsService`

- Fixes the support for the new backend system that was broken entirely
  (with and without events support).
- Migrates the `BitbucketCloudEntityProvider` to use the `EventsService`.

Signed-off-by: Patrick Jungermann <Patrick.Jungermann@gmail.com>
  • Loading branch information
pjungermann committed Feb 21, 2024
1 parent f5ec8a4 commit 155f921
Show file tree
Hide file tree
Showing 9 changed files with 141 additions and 73 deletions.
40 changes: 40 additions & 0 deletions .changeset/silver-flowers-trade.md
@@ -0,0 +1,40 @@
---
'@backstage/plugin-catalog-backend-module-bitbucket-cloud': minor
---

BREAKING CHANGE: Migrates the `BitbucketCloudEntityProvider` to use the `EventsService`; fix new backend system support.

`BitbucketCloudEntityProvider.fromConfig` accepts `events: EventsService` as optional argument to its `options`.
With provided `events`, the event-based updates/refresh will be available.
However, the `EventSubscriber` interface was removed including its `supportsEventTopics()` and `onEvent(params)`.

The event subscription happens on `connect(connection)` if the `events` is available.

**Migration:**

```diff
const bitbucketCloudProvider = BitbucketCloudEntityProvider.fromConfig(
env.config,
{
catalogApi: new CatalogClient({ discoveryApi: env.discovery }),
+ events: env.events,
logger: env.logger,
scheduler: env.scheduler,
tokenManager: env.tokenManager,
},
);
- env.eventBroker.subscribe(bitbucketCloudProvider);
```

**New Backend System:**

Before this change, using this module with the new backend system was broken.
Now, you can add the catalog module for Bitbucket Cloud incl. event support backend.
Event support will always be enabled.
However, no updates/refresh will happen without receiving events.

```ts
backend.add(
import('@backstage/plugin-catalog-backend-module-bitbucket-cloud/alpha'),
);
```
33 changes: 31 additions & 2 deletions docs/integrations/bitbucketCloud/discovery.md
Expand Up @@ -24,7 +24,35 @@ package.
yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-bitbucket-cloud
```

### Installation without Events Support
### 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-cloud/alpha'),
);
backend.add(
import('@backstage/plugin-catalog-backend-module-bitbucket-cloud/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:

- <https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md>
- <https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-aws-sqs/README.md>
- <https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-bitbucket-cloud/README.md>

### Installation with Legacy Backend System

#### Installation without Events Support

And then add the entity provider to your catalog builder:

Expand All @@ -49,7 +77,7 @@ export default async function createPlugin(
}
```

### Installation with Events Support
#### Installation with Events Support

Please follow the installation instructions at

Expand Down Expand Up @@ -83,6 +111,7 @@ export default async function createPlugin(
env.config,
{
catalogApi: new CatalogClient({ discoveryApi: env.discovery }),
events: env.events,
logger: env.logger,
scheduler: env.scheduler,
tokenManager: env.tokenManager,
Expand Down
18 changes: 6 additions & 12 deletions plugins/catalog-backend-module-bitbucket-cloud/api-report.md
Expand Up @@ -7,26 +7,24 @@ import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { EntityProvider } from '@backstage/plugin-catalog-node';
import { EntityProviderConnection } from '@backstage/plugin-catalog-node';
import { EventParams } from '@backstage/plugin-events-node';
import { Events } from '@backstage/plugin-bitbucket-cloud-common';
import { EventSubscriber } from '@backstage/plugin-events-node';
import { Logger } from 'winston';
import { EventsService } from '@backstage/plugin-events-node';
import { LoggerService } from '@backstage/backend-plugin-api';
import { PluginTaskScheduler } from '@backstage/backend-tasks';
import { TaskRunner } from '@backstage/backend-tasks';
import { TokenManager } from '@backstage/backend-common';

// @public
export class BitbucketCloudEntityProvider
implements EntityProvider, EventSubscriber
{
export class BitbucketCloudEntityProvider implements EntityProvider {
// (undocumented)
connect(connection: EntityProviderConnection): Promise<void>;
// (undocumented)
static fromConfig(
config: Config,
options: {
catalogApi?: CatalogApi;
logger: Logger;
events?: EventsService;
logger: LoggerService;
schedule?: TaskRunner;
scheduler?: PluginTaskScheduler;
tokenManager?: TokenManager;
Expand All @@ -37,12 +35,8 @@ export class BitbucketCloudEntityProvider
// (undocumented)
getTaskId(): string;
// (undocumented)
onEvent(params: EventParams): Promise<void>;
// (undocumented)
onRepoPush(event: Events.RepoPushEvent): Promise<void>;
// (undocumented)
refresh(logger: Logger): Promise<void>;
// (undocumented)
supportsEventTopics(): string[];
refresh(logger: LoggerService): Promise<void>;
}
```
4 changes: 2 additions & 2 deletions plugins/catalog-backend-module-bitbucket-cloud/package.json
Expand Up @@ -56,13 +56,13 @@
"@backstage/plugin-catalog-common": "workspace:^",
"@backstage/plugin-catalog-node": "workspace:^",
"@backstage/plugin-events-node": "workspace:^",
"uuid": "^8.0.0",
"winston": "^3.2.1"
"uuid": "^8.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"
},
Expand Down
Expand Up @@ -14,30 +14,36 @@
* limitations under the License.
*/

import { createServiceFactory } from '@backstage/backend-plugin-api';
import { TaskScheduleDefinition } from '@backstage/backend-tasks';
import { startTestBackend, mockServices } from '@backstage/backend-test-utils';
import { EntityProviderConnection } from '@backstage/plugin-catalog-node';
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha';
import { eventsExtensionPoint } from '@backstage/plugin-events-node/alpha';
import { TestEventsService } from '@backstage/plugin-events-backend-test-utils';
import { eventsServiceRef } from '@backstage/plugin-events-node';
import { Duration } from 'luxon';
import { catalogModuleBitbucketCloudEntityProvider } from './catalogModuleBitbucketCloudEntityProvider';
import { BitbucketCloudEntityProvider } from '../providers/BitbucketCloudEntityProvider';

describe('catalogModuleBitbucketCloudEntityProvider', () => {
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<BitbucketCloudEntityProvider> | undefined;
let addedSubscribers: Array<BitbucketCloudEntityProvider> | undefined;
let usedSchedule: TaskScheduleDefinition | undefined;

const catalogExtensionPointImpl = {
addEntityProvider: (providers: any) => {
addedProviders = providers;
},
};
const eventsExtensionPointImpl = {
addSubscribers: (subscribers: any) => {
addedSubscribers = subscribers;
},
};
const connection = jest.fn() as unknown as EntityProviderConnection;
const runner = jest.fn();
const scheduler = mockServices.scheduler.mock({
createScheduledTaskRunner(schedule) {
Expand All @@ -49,9 +55,9 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => {
await startTestBackend({
extensionPoints: [
[catalogProcessingExtensionPoint, catalogExtensionPointImpl],
[eventsExtensionPoint, eventsExtensionPointImpl],
],
features: [
eventsServiceFactory(),
catalogModuleBitbucketCloudEntityProvider(),
mockServices.rootConfig.factory({
data: {
Expand All @@ -75,10 +81,14 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => {
expect(usedSchedule?.frequency).toEqual(Duration.fromISO('P1M'));
expect(usedSchedule?.timeout).toEqual(Duration.fromISO('PT3M'));
expect(addedProviders?.length).toEqual(1);
expect(addedProviders?.pop()?.getProviderName()).toEqual(
expect(runner).not.toHaveBeenCalled();
const provider = addedProviders!.pop()!;
expect(provider.getProviderName()).toEqual(
'bitbucketCloud-provider:default',
);
expect(addedSubscribers).toEqual(addedProviders);
expect(runner).not.toHaveBeenCalled();
await provider.connect(connection);
expect(events.subscribed).toHaveLength(1);
expect(events.subscribed[0].id).toEqual('bitbucketCloud-provider:default');
expect(runner).toHaveBeenCalledTimes(1);
});
});
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import { loggerToWinstonLogger } from '@backstage/backend-common';
import {
coreServices,
createBackendModule,
Expand All @@ -23,7 +22,7 @@ import {
catalogProcessingExtensionPoint,
catalogServiceRef,
} from '@backstage/plugin-catalog-node/alpha';
import { eventsExtensionPoint } from '@backstage/plugin-events-node/alpha';
import { eventsServiceRef } from '@backstage/plugin-events-node';
import { BitbucketCloudEntityProvider } from '../providers/BitbucketCloudEntityProvider';

/**
Expand All @@ -38,9 +37,7 @@ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({
catalog: catalogProcessingExtensionPoint,
catalogApi: catalogServiceRef,
config: coreServices.rootConfig,
// TODO(pjungermann): How to make this optional for those which only want the provider without event support?
// Do we even want to support this?
events: eventsExtensionPoint,
events: eventsServiceRef,
logger: coreServices.logger,
scheduler: coreServices.scheduler,
tokenManager: coreServices.tokenManager,
Expand All @@ -54,16 +51,15 @@ export const catalogModuleBitbucketCloudEntityProvider = createBackendModule({
scheduler,
tokenManager,
}) {
const winstonLogger = loggerToWinstonLogger(logger);
const providers = BitbucketCloudEntityProvider.fromConfig(config, {
catalogApi,
logger: winstonLogger,
events,
logger,
scheduler,
tokenManager,
});

catalog.addEntityProvider(providers);
events.addSubscribers(providers);
},
});
},
Expand Down
Expand Up @@ -29,6 +29,7 @@ import {
locationSpecToLocationEntity,
} from '@backstage/plugin-catalog-node';
import { Events } from '@backstage/plugin-bitbucket-cloud-common';
import { DefaultEventsService } from '@backstage/plugin-events-node';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import {
Expand Down Expand Up @@ -436,6 +437,7 @@ describe('BitbucketCloudEntityProvider', () => {
'added-module/catalog-custom.yaml',
);

const events = DefaultEventsService.create({ logger });
const catalogApi = {
getEntities: async (
request: { filter: Record<string, string> },
Expand All @@ -457,6 +459,7 @@ describe('BitbucketCloudEntityProvider', () => {
};
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
catalogApi: catalogApi as any as CatalogApi,
events,
logger,
schedule,
tokenManager,
Expand Down Expand Up @@ -537,7 +540,7 @@ describe('BitbucketCloudEntityProvider', () => {
);

await provider.connect(entityProviderConnection);
await provider.onEvent(repoPushEventParams);
await events.publish(repoPushEventParams);

const addedEntities = [
{
Expand Down Expand Up @@ -566,31 +569,22 @@ describe('BitbucketCloudEntityProvider', () => {
});
});

it('onRepoPush fail on incomplete setup', async () => {
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
logger,
schedule,
})[0];

await expect(provider.onEvent(repoPushEventParams)).rejects.toThrow(
'bitbucketCloud-provider:myProvider not well configured to handle repo:push. Missing CatalogApi and/or TokenManager.',
);
});

it('no onRepoPush update on non-matching workspace slug', async () => {
const catalogApi = {
getEntities: jest.fn(),
refreshEntity: jest.fn(),
};
const events = DefaultEventsService.create({ logger });
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
catalogApi: catalogApi as any as CatalogApi,
events,
logger,
schedule,
tokenManager,
})[0];

await provider.connect(entityProviderConnection);
await provider.onEvent({
await events.publish({
...repoPushEventParams,
eventPayload: {
...repoPushEventParams.eventPayload,
Expand All @@ -613,15 +607,17 @@ describe('BitbucketCloudEntityProvider', () => {
getEntities: jest.fn(),
refreshEntity: jest.fn(),
};
const events = DefaultEventsService.create({ logger });
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
catalogApi: catalogApi as any as CatalogApi,
events,
logger,
schedule,
tokenManager,
})[0];

await provider.connect(entityProviderConnection);
await provider.onEvent({
await events.publish({
...repoPushEventParams,
eventPayload: {
...repoPushEventParams.eventPayload,
Expand Down

0 comments on commit 155f921

Please sign in to comment.