feat(catalog-backend-module-azure): add Azure DevOps SCM events bridge wiring
Signed-off-by: Lokesh Kaki <lokeshkaki1@gmail.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 2026 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 { LoggerService } from '@backstage/backend-plugin-api';
|
||||
import { CatalogScmEventsService } from '@backstage/plugin-catalog-node/alpha';
|
||||
import { EventParams, EventsService } from '@backstage/plugin-events-node';
|
||||
import { analyzeAzureDevOpsWebhookEvent } from './analyzeAzureDevOpsWebhookEvent';
|
||||
|
||||
/**
|
||||
* Takes Azure DevOps webhook events, analyzes them, and publishes them as
|
||||
* catalog SCM events that entity providers and others can subscribe to.
|
||||
*/
|
||||
export class AzureDevOpsScmEventsBridge {
|
||||
readonly #logger: LoggerService;
|
||||
readonly #events: EventsService;
|
||||
readonly #catalogScmEvents: CatalogScmEventsService;
|
||||
#shuttingDown: boolean;
|
||||
#pendingPublish: Promise<void> | undefined;
|
||||
|
||||
constructor(options: {
|
||||
logger: LoggerService;
|
||||
events: EventsService;
|
||||
catalogScmEvents: CatalogScmEventsService;
|
||||
}) {
|
||||
this.#logger = options.logger;
|
||||
this.#events = options.events;
|
||||
this.#catalogScmEvents = options.catalogScmEvents;
|
||||
this.#shuttingDown = false;
|
||||
}
|
||||
|
||||
async start() {
|
||||
await this.#events.subscribe({
|
||||
id: 'catalog-azure-devops-scm-events-bridge',
|
||||
topics: ['azureDevOps'],
|
||||
onEvent: this.#onEvent.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
async stop() {
|
||||
this.#shuttingDown = true;
|
||||
await this.#pendingPublish;
|
||||
}
|
||||
|
||||
async #onEvent(params: EventParams): Promise<void> {
|
||||
const eventPayload = params.eventPayload as
|
||||
| { eventType?: string }
|
||||
| undefined;
|
||||
const eventType = eventPayload?.eventType;
|
||||
if (!eventType || !eventPayload) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (this.#pendingPublish) {
|
||||
await this.#pendingPublish;
|
||||
}
|
||||
|
||||
if (this.#shuttingDown) {
|
||||
this.#logger.warn(
|
||||
`Skipping Azure DevOps webhook event of type "${eventType}" on topic "${params.topic}" because the bridge is shutting down`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.#pendingPublish = Promise.resolve().then(async () => {
|
||||
try {
|
||||
const output = await analyzeAzureDevOpsWebhookEvent(
|
||||
eventType,
|
||||
eventPayload,
|
||||
{
|
||||
isRelevantPath: path =>
|
||||
path.endsWith('.yaml') || path.endsWith('.yml'),
|
||||
},
|
||||
);
|
||||
|
||||
if (output.result === 'ok') {
|
||||
await this.#catalogScmEvents.publish(output.events);
|
||||
} else if (output.result === 'ignored') {
|
||||
this.#logger.debug(
|
||||
`Skipping Azure DevOps webhook event of type "${eventType}" on topic "${params.topic}" because it is ignored: ${output.reason}`,
|
||||
);
|
||||
} else if (output.result === 'aborted') {
|
||||
this.#logger.warn(
|
||||
`Skipping Azure DevOps webhook event of type "${eventType}" on topic "${params.topic}" because it is aborted: ${output.reason}`,
|
||||
);
|
||||
} else if (output.result === 'unsupported-event') {
|
||||
this.#logger.debug(
|
||||
`Skipping Azure DevOps webhook event of type "${eventType}" on topic "${params.topic}" because it is unsupported: ${output.event}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.#logger.warn(
|
||||
`Failed to handle Azure DevOps webhook event of type "${eventType}"`,
|
||||
error,
|
||||
);
|
||||
} finally {
|
||||
this.#pendingPublish = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
await this.#pendingPublish;
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -19,10 +19,13 @@ import {
|
||||
createBackendModule,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node';
|
||||
import { catalogScmEventsServiceRef } from '@backstage/plugin-catalog-node/alpha';
|
||||
import { eventsServiceRef } from '@backstage/plugin-events-node';
|
||||
import {
|
||||
AzureBlobStorageEntityProvider,
|
||||
AzureDevOpsEntityProvider,
|
||||
} from '../providers';
|
||||
import { AzureDevOpsScmEventsBridge } from '../events/AzureDevOpsScmEventsBridge';
|
||||
|
||||
/**
|
||||
* Registers the AzureDevOpsEntityProvider with the catalog processing extension point.
|
||||
@@ -39,8 +42,19 @@ export const catalogModuleAzureEntityProvider = createBackendModule({
|
||||
catalog: catalogProcessingExtensionPoint,
|
||||
logger: coreServices.logger,
|
||||
scheduler: coreServices.scheduler,
|
||||
events: eventsServiceRef,
|
||||
catalogScmEvents: catalogScmEventsServiceRef,
|
||||
lifecycle: coreServices.lifecycle,
|
||||
},
|
||||
async init({ config, catalog, logger, scheduler }) {
|
||||
async init({
|
||||
config,
|
||||
catalog,
|
||||
logger,
|
||||
scheduler,
|
||||
events,
|
||||
catalogScmEvents,
|
||||
lifecycle,
|
||||
}) {
|
||||
// Check for Azure Blob Storage provider configuration and register it
|
||||
if (config.has('catalog.providers.azureBlob')) {
|
||||
catalog.addEntityProvider(
|
||||
@@ -60,6 +74,18 @@ export const catalogModuleAzureEntityProvider = createBackendModule({
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const bridge = new AzureDevOpsScmEventsBridge({
|
||||
logger,
|
||||
events,
|
||||
catalogScmEvents,
|
||||
});
|
||||
lifecycle.addStartupHook(async () => {
|
||||
await bridge.start();
|
||||
});
|
||||
lifecycle.addShutdownHook(async () => {
|
||||
await bridge.stop();
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user