Refactor Slack Notifications Backend Module to use MetricsApi

Signed-off-by: Gabriel Dugny <gabriel.dugny@believe.com>
This commit is contained in:
Gabriel Dugny
2026-03-14 13:48:07 +01:00
parent 6f01c33a0e
commit e7c6c32f26
6 changed files with 49 additions and 9 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-notifications-backend-module-slack': patch
---
The Slack notification processor now uses the `MetricsService` to create metrics, providing plugin-scoped attribution.
@@ -42,7 +42,6 @@
"@backstage/plugin-notifications-common": "workspace:^",
"@backstage/plugin-notifications-node": "workspace:^",
"@backstage/types": "workspace:^",
"@opentelemetry/api": "^1.9.0",
"@slack/bolt": "^3.21.4",
"@slack/types": "^2.14.0",
"@slack/web-api": "^7.5.0",
@@ -15,6 +15,7 @@
*/
import { mockServices } from '@backstage/backend-test-utils';
import { metricsServiceMock } from '@backstage/backend-test-utils/alpha';
import { SlackNotificationProcessor } from './SlackNotificationProcessor';
import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils';
import { KnownBlock, WebClient } from '@slack/web-api';
@@ -125,6 +126,7 @@ const DEFAULT_ENTITIES_RESPONSE = {
describe('SlackNotificationProcessor', () => {
const logger = mockServices.logger.mock();
const auth = mockServices.auth();
const metrics = metricsServiceMock.mock();
const config = mockServices.rootConfig({
data: {
app: {
@@ -157,6 +159,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -224,6 +227,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
blockKitRenderer: () => customBlocks,
})[0];
@@ -256,6 +260,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -331,6 +336,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -365,6 +371,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -410,6 +417,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -465,6 +473,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -529,6 +538,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -584,6 +594,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -639,6 +650,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -694,6 +706,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -750,6 +763,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -809,6 +823,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -863,6 +878,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -921,6 +937,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -959,6 +976,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -982,6 +1000,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: [DEFAULT_ENTITIES_RESPONSE.items[2]],
}),
metrics,
slack,
})[0];
@@ -1021,6 +1040,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1066,6 +1086,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1125,6 +1146,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1204,6 +1226,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1298,6 +1321,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
},
)[0];
@@ -1375,6 +1399,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1441,6 +1466,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1481,6 +1507,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1520,6 +1547,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
})[0];
@@ -1568,6 +1596,7 @@ describe('SlackNotificationProcessor', () => {
catalog: catalogServiceMock({
entities: DEFAULT_ENTITIES_RESPONSE.items,
}),
metrics,
slack,
},
)[0];
@@ -15,6 +15,10 @@
*/
import { AuthService, LoggerService } from '@backstage/backend-plugin-api';
import {
MetricsService,
MetricsServiceCounter,
} from '@backstage/backend-plugin-api/alpha';
import {
Entity,
isUserEntity,
@@ -30,7 +34,6 @@ import {
NotificationSendOptions,
} from '@backstage/plugin-notifications-node';
import { durationToMilliseconds } from '@backstage/types';
import { Counter, metrics } from '@opentelemetry/api';
import { ChatPostMessageArguments, WebClient } from '@slack/web-api';
import DataLoader from 'dataloader';
import pThrottle from 'p-throttle';
@@ -48,8 +51,8 @@ export class SlackNotificationProcessor implements NotificationProcessor {
private readonly sendNotifications: (
opts: ChatPostMessageArguments[],
) => Promise<void>;
private readonly messagesSent: Counter;
private readonly messagesFailed: Counter;
private readonly messagesSent: MetricsServiceCounter;
private readonly messagesFailed: MetricsServiceCounter;
private readonly broadcastChannels?: string[];
private readonly broadcastRoutes?: BroadcastRoute[];
private readonly entityLoader: DataLoader<string, Entity | undefined>;
@@ -64,6 +67,7 @@ export class SlackNotificationProcessor implements NotificationProcessor {
auth: AuthService;
logger: LoggerService;
catalog: CatalogService;
metrics: MetricsService;
slack?: WebClient;
broadcastChannels?: string[];
blockKitRenderer?: SlackBlockKitRenderer;
@@ -103,6 +107,7 @@ export class SlackNotificationProcessor implements NotificationProcessor {
auth: AuthService;
logger: LoggerService;
catalog: CatalogService;
metrics: MetricsService;
broadcastChannels?: string[];
broadcastRoutes?: BroadcastRoute[];
username?: string;
@@ -114,6 +119,7 @@ export class SlackNotificationProcessor implements NotificationProcessor {
auth,
catalog,
logger,
metrics,
slack,
broadcastChannels,
broadcastRoutes,
@@ -159,14 +165,13 @@ export class SlackNotificationProcessor implements NotificationProcessor {
},
);
const meter = metrics.getMeter('default');
this.messagesSent = meter.createCounter(
this.messagesSent = metrics.createCounter(
'notifications.processors.slack.sent.count',
{
description: 'Number of messages sent to Slack successfully',
},
);
this.messagesFailed = meter.createCounter(
this.messagesFailed = metrics.createCounter(
'notifications.processors.slack.error.count',
{
description: 'Number of messages that failed to send to Slack',
@@ -17,6 +17,7 @@ import {
coreServices,
createBackendModule,
} from '@backstage/backend-plugin-api';
import { metricsServiceRef } from '@backstage/backend-plugin-api/alpha';
import { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node';
import { SlackNotificationProcessor } from './lib/SlackNotificationProcessor';
import { catalogServiceRef } from '@backstage/plugin-catalog-node';
@@ -52,13 +53,15 @@ export const notificationsModuleSlack = createBackendModule({
logger: coreServices.logger,
catalog: catalogServiceRef,
notifications: notificationsProcessingExtensionPoint,
metrics: metricsServiceRef,
},
async init({ auth, config, logger, catalog, notifications }) {
async init({ auth, config, logger, catalog, notifications, metrics }) {
notifications.addProcessor(
SlackNotificationProcessor.fromConfig(config, {
auth,
logger,
catalog,
metrics,
blockKitRenderer,
}),
);
-1
View File
@@ -5910,7 +5910,6 @@ __metadata:
"@backstage/test-utils": "workspace:^"
"@backstage/types": "workspace:^"
"@faker-js/faker": "npm:^10.0.0"
"@opentelemetry/api": "npm:^1.9.0"
"@slack/bolt": "npm:^3.21.4"
"@slack/types": "npm:^2.14.0"
"@slack/web-api": "npm:^7.5.0"