fix(events,catalog,bitbucket-cloud)!: fix new backend system support; 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>
This commit is contained in:
@@ -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'),
|
||||
);
|
||||
```
|
||||
@@ -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:
|
||||
|
||||
@@ -49,7 +77,7 @@ export default async function createPlugin(
|
||||
}
|
||||
```
|
||||
|
||||
### Installation with Events Support
|
||||
#### Installation with Events Support
|
||||
|
||||
Please follow the installation instructions at
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -7,18 +7,15 @@ 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)
|
||||
@@ -26,7 +23,8 @@ export class BitbucketCloudEntityProvider
|
||||
config: Config,
|
||||
options: {
|
||||
catalogApi?: CatalogApi;
|
||||
logger: Logger;
|
||||
events?: EventsService;
|
||||
logger: LoggerService;
|
||||
schedule?: TaskRunner;
|
||||
scheduler?: PluginTaskScheduler;
|
||||
tokenManager?: TokenManager;
|
||||
@@ -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>;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -56,13 +56,13 @@
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-catalog-node": "workspace:^",
|
||||
"@backstage/plugin-events-node": "workspace:^",
|
||||
"uuid": "^9.0.0",
|
||||
"winston": "^3.2.1"
|
||||
"uuid": "^9.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"
|
||||
},
|
||||
|
||||
+21
-11
@@ -14,18 +14,28 @@
|
||||
* 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 = {
|
||||
@@ -33,11 +43,7 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => {
|
||||
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) {
|
||||
@@ -49,9 +55,9 @@ describe('catalogModuleBitbucketCloudEntityProvider', () => {
|
||||
await startTestBackend({
|
||||
extensionPoints: [
|
||||
[catalogProcessingExtensionPoint, catalogExtensionPointImpl],
|
||||
[eventsExtensionPoint, eventsExtensionPointImpl],
|
||||
],
|
||||
features: [
|
||||
eventsServiceFactory(),
|
||||
catalogModuleBitbucketCloudEntityProvider(),
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
+4
-8
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { loggerToWinstonLogger } from '@backstage/backend-common';
|
||||
import {
|
||||
coreServices,
|
||||
createBackendModule,
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
@@ -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,
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
+10
-14
@@ -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 {
|
||||
@@ -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> },
|
||||
@@ -457,6 +459,7 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
};
|
||||
const provider = BitbucketCloudEntityProvider.fromConfig(defaultConfig, {
|
||||
catalogApi: catalogApi as any as CatalogApi,
|
||||
events,
|
||||
logger,
|
||||
schedule,
|
||||
tokenManager,
|
||||
@@ -537,7 +540,7 @@ describe('BitbucketCloudEntityProvider', () => {
|
||||
);
|
||||
|
||||
await provider.connect(entityProviderConnection);
|
||||
await provider.onEvent(repoPushEventParams);
|
||||
await events.publish(repoPushEventParams);
|
||||
|
||||
const addedEntities = [
|
||||
{
|
||||
@@ -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,
|
||||
@@ -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,
|
||||
|
||||
+26
-23
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { TokenManager } from '@backstage/backend-common';
|
||||
import { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { LocationEntity } from '@backstage/catalog-model';
|
||||
@@ -35,13 +36,12 @@ import {
|
||||
locationSpecToLocationEntity,
|
||||
} from '@backstage/plugin-catalog-node';
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-common';
|
||||
import { EventParams, EventSubscriber } from '@backstage/plugin-events-node';
|
||||
import { EventsService } from '@backstage/plugin-events-node';
|
||||
import {
|
||||
BitbucketCloudEntityProviderConfig,
|
||||
readProviderConfigs,
|
||||
} from './BitbucketCloudEntityProviderConfig';
|
||||
import * as uuid from 'uuid';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
const DEFAULT_BRANCH = 'master';
|
||||
const TOPIC_REPO_PUSH = 'bitbucketCloud.repo:push';
|
||||
@@ -62,14 +62,13 @@ interface IngestionTarget {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class BitbucketCloudEntityProvider
|
||||
implements EntityProvider, EventSubscriber
|
||||
{
|
||||
export class BitbucketCloudEntityProvider implements EntityProvider {
|
||||
private readonly client: BitbucketCloudClient;
|
||||
private readonly config: BitbucketCloudEntityProviderConfig;
|
||||
private readonly logger: Logger;
|
||||
private readonly logger: LoggerService;
|
||||
private readonly scheduleFn: () => Promise<void>;
|
||||
private readonly catalogApi?: CatalogApi;
|
||||
private readonly events?: EventsService;
|
||||
private readonly tokenManager?: TokenManager;
|
||||
private connection?: EntityProviderConnection;
|
||||
|
||||
@@ -79,7 +78,8 @@ export class BitbucketCloudEntityProvider
|
||||
config: Config,
|
||||
options: {
|
||||
catalogApi?: CatalogApi;
|
||||
logger: Logger;
|
||||
events?: EventsService;
|
||||
logger: LoggerService;
|
||||
schedule?: TaskRunner;
|
||||
scheduler?: PluginTaskScheduler;
|
||||
tokenManager?: TokenManager;
|
||||
@@ -114,6 +114,7 @@ export class BitbucketCloudEntityProvider
|
||||
options.logger,
|
||||
taskRunner,
|
||||
options.catalogApi,
|
||||
options.events,
|
||||
options.tokenManager,
|
||||
);
|
||||
});
|
||||
@@ -122,9 +123,10 @@ export class BitbucketCloudEntityProvider
|
||||
private constructor(
|
||||
config: BitbucketCloudEntityProviderConfig,
|
||||
integration: BitbucketCloudIntegration,
|
||||
logger: Logger,
|
||||
logger: LoggerService,
|
||||
taskRunner: TaskRunner,
|
||||
catalogApi?: CatalogApi,
|
||||
events?: EventsService,
|
||||
tokenManager?: TokenManager,
|
||||
) {
|
||||
this.client = BitbucketCloudClient.fromConfig(integration.config);
|
||||
@@ -134,6 +136,7 @@ export class BitbucketCloudEntityProvider
|
||||
});
|
||||
this.scheduleFn = this.createScheduleFn(taskRunner);
|
||||
this.catalogApi = catalogApi;
|
||||
this.events = events;
|
||||
this.tokenManager = tokenManager;
|
||||
}
|
||||
|
||||
@@ -176,9 +179,23 @@ export class BitbucketCloudEntityProvider
|
||||
async connect(connection: EntityProviderConnection): Promise<void> {
|
||||
this.connection = connection;
|
||||
await this.scheduleFn();
|
||||
|
||||
if (this.events) {
|
||||
await this.events.subscribe({
|
||||
id: this.getProviderName(),
|
||||
topics: [TOPIC_REPO_PUSH],
|
||||
onEvent: async params => {
|
||||
if (params.topic !== TOPIC_REPO_PUSH) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.onRepoPush(params.eventPayload as Events.RepoPushEvent);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async refresh(logger: Logger) {
|
||||
async refresh(logger: LoggerService) {
|
||||
if (!this.connection) {
|
||||
throw new Error('Not initialized');
|
||||
}
|
||||
@@ -198,20 +215,6 @@ export class BitbucketCloudEntityProvider
|
||||
);
|
||||
}
|
||||
|
||||
/** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.supportsEventTopics} */
|
||||
supportsEventTopics(): string[] {
|
||||
return [TOPIC_REPO_PUSH];
|
||||
}
|
||||
|
||||
/** {@inheritdoc @backstage/plugin-events-node#EventSubscriber.onEvent} */
|
||||
async onEvent(params: EventParams): Promise<void> {
|
||||
if (params.topic !== TOPIC_REPO_PUSH) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.onRepoPush(params.eventPayload as Events.RepoPushEvent);
|
||||
}
|
||||
|
||||
private canHandleEvents(): boolean {
|
||||
if (this.catalogApi && this.tokenManager) {
|
||||
return true;
|
||||
|
||||
@@ -5303,11 +5303,11 @@ __metadata:
|
||||
"@backstage/plugin-bitbucket-cloud-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-catalog-node": "workspace:^"
|
||||
"@backstage/plugin-events-backend-test-utils": "workspace:^"
|
||||
"@backstage/plugin-events-node": "workspace:^"
|
||||
luxon: ^3.0.0
|
||||
msw: ^1.0.0
|
||||
uuid: ^9.0.0
|
||||
winston: ^3.2.1
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
|
||||
Reference in New Issue
Block a user