@backstage/plugin-notifications-backend-module-slack 0.1.0 → 0.1.1-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @backstage/plugin-notifications-backend-module-slack
2
2
 
3
+ ## 0.1.1-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - a1c5bbb: Added email-based Slack User ID lookup if `metadata.annotations.slack.com/bot-notify` is missing from user entity
8
+ - Updated dependencies
9
+ - @backstage/backend-plugin-api@1.3.1-next.1
10
+ - @backstage/catalog-client@1.10.0-next.0
11
+ - @backstage/catalog-model@1.7.3
12
+ - @backstage/config@1.3.2
13
+ - @backstage/errors@1.2.7
14
+ - @backstage/types@1.2.1
15
+ - @backstage/plugin-notifications-common@0.0.8
16
+ - @backstage/plugin-notifications-node@0.2.15-next.1
17
+
18
+ ## 0.1.1-next.0
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies
23
+ - @backstage/catalog-client@1.10.0-next.0
24
+ - @backstage/backend-plugin-api@1.3.1-next.0
25
+ - @backstage/plugin-notifications-node@0.2.15-next.0
26
+ - @backstage/catalog-model@1.7.3
27
+ - @backstage/config@1.3.2
28
+ - @backstage/errors@1.2.7
29
+ - @backstage/types@1.2.1
30
+ - @backstage/plugin-notifications-common@0.0.8
31
+
3
32
  ## 0.1.0
4
33
 
5
34
  ### Minor Changes
@@ -170,7 +170,11 @@ class SlackNotificationProcessor {
170
170
  const response = await this.catalog.getEntitiesByRefs(
171
171
  {
172
172
  entityRefs: entityRefs.slice(),
173
- fields: [`metadata.annotations.${constants.ANNOTATION_SLACK_BOT_NOTIFY}`]
173
+ fields: [
174
+ `kind`,
175
+ `spec.profile.email`,
176
+ `metadata.annotations.${constants.ANNOTATION_SLACK_BOT_NOTIFY}`
177
+ ]
174
178
  },
175
179
  {
176
180
  token
@@ -187,7 +191,33 @@ class SlackNotificationProcessor {
187
191
  console.log(`Entity not found: ${entityRef}`);
188
192
  throw new errors.NotFoundError(`Entity not found: ${entityRef}`);
189
193
  }
190
- return entity?.metadata?.annotations?.[constants.ANNOTATION_SLACK_BOT_NOTIFY];
194
+ const slackId = await this.resolveSlackId(entity);
195
+ return slackId;
196
+ }
197
+ async resolveSlackId(entity) {
198
+ const slackId = entity.metadata?.annotations?.[constants.ANNOTATION_SLACK_BOT_NOTIFY];
199
+ if (slackId) {
200
+ return slackId;
201
+ }
202
+ if (catalogModel.isUserEntity(entity)) {
203
+ return this.findSlackIdByEmail(entity);
204
+ }
205
+ return void 0;
206
+ }
207
+ async findSlackIdByEmail(entity) {
208
+ const email = entity.spec?.profile?.email;
209
+ if (!email) {
210
+ return void 0;
211
+ }
212
+ try {
213
+ const user = await this.slack.users.lookupByEmail({ email });
214
+ return user.user?.id;
215
+ } catch (error) {
216
+ this.logger.warn(
217
+ `Failed to lookup Slack user by email ${email}: ${error}`
218
+ );
219
+ return void 0;
220
+ }
191
221
  }
192
222
  async sendNotification(args) {
193
223
  const response = await this.slack.chat.postMessage(args);
@@ -1 +1 @@
1
- {"version":3,"file":"SlackNotificationProcessor.cjs.js","sources":["../../src/lib/SlackNotificationProcessor.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthService,\n DiscoveryService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport { Entity, parseEntityRef } from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport {\n NotificationProcessor,\n NotificationSendOptions,\n} from '@backstage/plugin-notifications-node';\nimport { durationToMilliseconds } from '@backstage/types';\nimport { Counter, metrics } from '@opentelemetry/api';\nimport { ChatPostMessageArguments, WebClient } from '@slack/web-api';\nimport DataLoader from 'dataloader';\nimport pThrottle from 'p-throttle';\nimport { ANNOTATION_SLACK_BOT_NOTIFY } from './constants';\nimport { toChatPostMessageArgs } from './util';\n\nexport class SlackNotificationProcessor implements NotificationProcessor {\n private readonly logger: LoggerService;\n private readonly catalog: CatalogApi;\n private readonly auth: AuthService;\n private readonly slack: WebClient;\n private readonly sendNotifications;\n private readonly messagesSent: Counter;\n private readonly messagesFailed: Counter;\n private readonly broadcastChannels?: string[];\n\n static fromConfig(\n config: Config,\n options: {\n auth: AuthService;\n discovery: DiscoveryService;\n logger: LoggerService;\n catalog: CatalogApi;\n slack?: WebClient;\n broadcastChannels?: string[];\n },\n ): SlackNotificationProcessor[] {\n const slackConfig =\n config.getOptionalConfigArray('notifications.processors.slack') ?? [];\n return slackConfig.map(c => {\n const token = c.getString('token');\n const slack = options.slack ?? new WebClient(token);\n const broadcastChannels = c.getOptionalStringArray('broadcastChannels');\n return new SlackNotificationProcessor({\n slack,\n broadcastChannels,\n ...options,\n });\n });\n }\n\n private constructor(options: {\n slack: WebClient;\n auth: AuthService;\n discovery: DiscoveryService;\n logger: LoggerService;\n catalog: CatalogApi;\n broadcastChannels?: string[];\n }) {\n const { auth, catalog, logger, slack, broadcastChannels } = options;\n this.logger = logger;\n this.catalog = catalog;\n this.auth = auth;\n this.slack = slack;\n this.broadcastChannels = broadcastChannels;\n\n const meter = metrics.getMeter('default');\n this.messagesSent = meter.createCounter(\n 'notifications.processors.slack.sent.count',\n {\n description: 'Number of messages sent to Slack successfully',\n },\n );\n this.messagesFailed = meter.createCounter(\n 'notifications.processors.slack.error.count',\n {\n description: 'Number of messages that failed to send to Slack',\n },\n );\n\n const throttle = pThrottle({\n limit: 10,\n interval: durationToMilliseconds({ minutes: 1 }),\n });\n const throttled = throttle((opts: ChatPostMessageArguments) =>\n this.sendNotification(opts),\n );\n this.sendNotifications = async (opts: ChatPostMessageArguments[]) => {\n const results = await Promise.allSettled(\n opts.map(message => throttled(message)),\n );\n\n let successCount = 0;\n let failureCount = 0;\n\n results.forEach(result => {\n if (result.status === 'fulfilled') {\n successCount++;\n } else {\n this.logger.error(\n `Failed to send Slack channel notification: ${result.reason.message}`,\n );\n failureCount++;\n }\n });\n\n this.messagesSent.add(successCount);\n this.messagesFailed.add(failureCount);\n };\n }\n\n getName(): string {\n return 'SlackNotificationProcessor';\n }\n\n async processOptions(\n options: NotificationSendOptions,\n ): Promise<NotificationSendOptions> {\n if (options.recipients.type !== 'entity') {\n return options;\n }\n\n const entityRefs = [options.recipients.entityRef].flat();\n\n const outbound: ChatPostMessageArguments[] = [];\n await Promise.all(\n entityRefs.map(async entityRef => {\n const compoundEntityRef = parseEntityRef(entityRef);\n // skip users as they are sent direct messages\n if (compoundEntityRef.kind === 'user') {\n return;\n }\n\n let channel;\n try {\n channel = await this.getSlackNotificationTarget(entityRef);\n } catch (error) {\n this.logger.error(\n `Failed to get Slack channel for entity: ${\n (error as Error).message\n }`,\n );\n return;\n }\n\n if (!channel) {\n this.logger.debug(`No Slack channel found for entity: ${entityRef}`);\n return;\n }\n\n this.logger.debug(\n `Sending notification with payload: ${JSON.stringify(\n options.payload,\n )}`,\n );\n\n const payload = toChatPostMessageArgs({\n channel,\n payload: options.payload,\n });\n\n this.logger.debug(\n `Sending Slack channel notification: ${JSON.stringify(payload)}`,\n );\n outbound.push(payload);\n }),\n );\n\n console.log('dispatching message');\n await this.sendNotifications(outbound);\n\n return options;\n }\n\n async postProcess(\n notification: Notification,\n options: NotificationSendOptions,\n ): Promise<void> {\n const destinations: string[] = [];\n\n // Handle broadcast case\n if (notification.user === null) {\n destinations.push(...(this.broadcastChannels ?? []));\n } else if (options.recipients.type === 'entity') {\n // Handle user-specific notification\n const entityRefs = [options.recipients.entityRef].flat();\n if (entityRefs.some(e => parseEntityRef(e).kind === 'group')) {\n // We've already dispatched a slack channel message, so let's not send a DM.\n return;\n }\n\n const destination = await this.getSlackNotificationTarget(\n notification.user,\n );\n\n if (!destination) {\n this.logger.error(\n `No slack.com/bot-notify annotation found for user: ${notification.user}`,\n );\n return;\n }\n\n destinations.push(destination);\n }\n\n // If no destinations, nothing to do\n if (destinations.length === 0) {\n return;\n }\n\n // Prepare outbound messages\n const outbound = destinations.map(channel =>\n toChatPostMessageArgs({ channel, payload: options.payload }),\n );\n\n // Log debug info\n outbound.forEach(payload => {\n this.logger.debug(`Sending notification: ${JSON.stringify(payload)}`);\n });\n\n // Send notifications\n await this.sendNotifications(outbound);\n }\n\n async getEntities(\n entityRefs: readonly string[],\n ): Promise<(Entity | undefined)[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const response = await this.catalog.getEntitiesByRefs(\n {\n entityRefs: entityRefs.slice(),\n fields: [`metadata.annotations.${ANNOTATION_SLACK_BOT_NOTIFY}`],\n },\n {\n token,\n },\n );\n\n return response.items;\n }\n\n async getSlackNotificationTarget(\n entityRef: string,\n ): Promise<string | undefined> {\n const entityLoader = new DataLoader<string, Entity | undefined>(\n entityRefs => this.getEntities(entityRefs),\n );\n const entity = await entityLoader.load(entityRef);\n\n if (!entity) {\n console.log(`Entity not found: ${entityRef}`);\n throw new NotFoundError(`Entity not found: ${entityRef}`);\n }\n\n return entity?.metadata?.annotations?.[ANNOTATION_SLACK_BOT_NOTIFY];\n }\n\n async sendNotification(args: ChatPostMessageArguments): Promise<void> {\n const response = await this.slack.chat.postMessage(args);\n\n if (!response.ok) {\n throw new Error(`Failed to send notification: ${response.error}`);\n }\n }\n}\n"],"names":["WebClient","metrics","pThrottle","durationToMilliseconds","parseEntityRef","toChatPostMessageArgs","ANNOTATION_SLACK_BOT_NOTIFY","DataLoader","NotFoundError"],"mappings":"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"}
1
+ {"version":3,"file":"SlackNotificationProcessor.cjs.js","sources":["../../src/lib/SlackNotificationProcessor.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AuthService,\n DiscoveryService,\n LoggerService,\n} from '@backstage/backend-plugin-api';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport {\n Entity,\n isUserEntity,\n parseEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { Config } from '@backstage/config';\nimport { NotFoundError } from '@backstage/errors';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport {\n NotificationProcessor,\n NotificationSendOptions,\n} from '@backstage/plugin-notifications-node';\nimport { durationToMilliseconds } from '@backstage/types';\nimport { Counter, metrics } from '@opentelemetry/api';\nimport { ChatPostMessageArguments, WebClient } from '@slack/web-api';\nimport DataLoader from 'dataloader';\nimport pThrottle from 'p-throttle';\nimport { ANNOTATION_SLACK_BOT_NOTIFY } from './constants';\nimport { toChatPostMessageArgs } from './util';\n\nexport class SlackNotificationProcessor implements NotificationProcessor {\n private readonly logger: LoggerService;\n private readonly catalog: CatalogApi;\n private readonly auth: AuthService;\n private readonly slack: WebClient;\n private readonly sendNotifications;\n private readonly messagesSent: Counter;\n private readonly messagesFailed: Counter;\n private readonly broadcastChannels?: string[];\n\n static fromConfig(\n config: Config,\n options: {\n auth: AuthService;\n discovery: DiscoveryService;\n logger: LoggerService;\n catalog: CatalogApi;\n slack?: WebClient;\n broadcastChannels?: string[];\n },\n ): SlackNotificationProcessor[] {\n const slackConfig =\n config.getOptionalConfigArray('notifications.processors.slack') ?? [];\n return slackConfig.map(c => {\n const token = c.getString('token');\n const slack = options.slack ?? new WebClient(token);\n const broadcastChannels = c.getOptionalStringArray('broadcastChannels');\n return new SlackNotificationProcessor({\n slack,\n broadcastChannels,\n ...options,\n });\n });\n }\n\n private constructor(options: {\n slack: WebClient;\n auth: AuthService;\n discovery: DiscoveryService;\n logger: LoggerService;\n catalog: CatalogApi;\n broadcastChannels?: string[];\n }) {\n const { auth, catalog, logger, slack, broadcastChannels } = options;\n this.logger = logger;\n this.catalog = catalog;\n this.auth = auth;\n this.slack = slack;\n this.broadcastChannels = broadcastChannels;\n\n const meter = metrics.getMeter('default');\n this.messagesSent = meter.createCounter(\n 'notifications.processors.slack.sent.count',\n {\n description: 'Number of messages sent to Slack successfully',\n },\n );\n this.messagesFailed = meter.createCounter(\n 'notifications.processors.slack.error.count',\n {\n description: 'Number of messages that failed to send to Slack',\n },\n );\n\n const throttle = pThrottle({\n limit: 10,\n interval: durationToMilliseconds({ minutes: 1 }),\n });\n const throttled = throttle((opts: ChatPostMessageArguments) =>\n this.sendNotification(opts),\n );\n this.sendNotifications = async (opts: ChatPostMessageArguments[]) => {\n const results = await Promise.allSettled(\n opts.map(message => throttled(message)),\n );\n\n let successCount = 0;\n let failureCount = 0;\n\n results.forEach(result => {\n if (result.status === 'fulfilled') {\n successCount++;\n } else {\n this.logger.error(\n `Failed to send Slack channel notification: ${result.reason.message}`,\n );\n failureCount++;\n }\n });\n\n this.messagesSent.add(successCount);\n this.messagesFailed.add(failureCount);\n };\n }\n\n getName(): string {\n return 'SlackNotificationProcessor';\n }\n\n async processOptions(\n options: NotificationSendOptions,\n ): Promise<NotificationSendOptions> {\n if (options.recipients.type !== 'entity') {\n return options;\n }\n\n const entityRefs = [options.recipients.entityRef].flat();\n\n const outbound: ChatPostMessageArguments[] = [];\n await Promise.all(\n entityRefs.map(async entityRef => {\n const compoundEntityRef = parseEntityRef(entityRef);\n // skip users as they are sent direct messages\n if (compoundEntityRef.kind === 'user') {\n return;\n }\n\n let channel;\n try {\n channel = await this.getSlackNotificationTarget(entityRef);\n } catch (error) {\n this.logger.error(\n `Failed to get Slack channel for entity: ${\n (error as Error).message\n }`,\n );\n return;\n }\n\n if (!channel) {\n this.logger.debug(`No Slack channel found for entity: ${entityRef}`);\n return;\n }\n\n this.logger.debug(\n `Sending notification with payload: ${JSON.stringify(\n options.payload,\n )}`,\n );\n\n const payload = toChatPostMessageArgs({\n channel,\n payload: options.payload,\n });\n\n this.logger.debug(\n `Sending Slack channel notification: ${JSON.stringify(payload)}`,\n );\n outbound.push(payload);\n }),\n );\n\n console.log('dispatching message');\n await this.sendNotifications(outbound);\n\n return options;\n }\n\n async postProcess(\n notification: Notification,\n options: NotificationSendOptions,\n ): Promise<void> {\n const destinations: string[] = [];\n\n // Handle broadcast case\n if (notification.user === null) {\n destinations.push(...(this.broadcastChannels ?? []));\n } else if (options.recipients.type === 'entity') {\n // Handle user-specific notification\n const entityRefs = [options.recipients.entityRef].flat();\n if (entityRefs.some(e => parseEntityRef(e).kind === 'group')) {\n // We've already dispatched a slack channel message, so let's not send a DM.\n return;\n }\n\n const destination = await this.getSlackNotificationTarget(\n notification.user,\n );\n\n if (!destination) {\n this.logger.error(\n `No slack.com/bot-notify annotation found for user: ${notification.user}`,\n );\n return;\n }\n\n destinations.push(destination);\n }\n\n // If no destinations, nothing to do\n if (destinations.length === 0) {\n return;\n }\n\n // Prepare outbound messages\n const outbound = destinations.map(channel =>\n toChatPostMessageArgs({ channel, payload: options.payload }),\n );\n\n // Log debug info\n outbound.forEach(payload => {\n this.logger.debug(`Sending notification: ${JSON.stringify(payload)}`);\n });\n\n // Send notifications\n await this.sendNotifications(outbound);\n }\n\n async getEntities(\n entityRefs: readonly string[],\n ): Promise<(Entity | undefined)[]> {\n const { token } = await this.auth.getPluginRequestToken({\n onBehalfOf: await this.auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n const response = await this.catalog.getEntitiesByRefs(\n {\n entityRefs: entityRefs.slice(),\n fields: [\n `kind`,\n `spec.profile.email`,\n `metadata.annotations.${ANNOTATION_SLACK_BOT_NOTIFY}`,\n ],\n },\n {\n token,\n },\n );\n\n return response.items;\n }\n\n async getSlackNotificationTarget(\n entityRef: string,\n ): Promise<string | undefined> {\n const entityLoader = new DataLoader<string, Entity | undefined>(\n entityRefs => this.getEntities(entityRefs),\n );\n const entity = await entityLoader.load(entityRef);\n\n if (!entity) {\n console.log(`Entity not found: ${entityRef}`);\n throw new NotFoundError(`Entity not found: ${entityRef}`);\n }\n\n const slackId = await this.resolveSlackId(entity);\n return slackId;\n }\n\n private async resolveSlackId(entity: Entity): Promise<string | undefined> {\n // First try to get Slack ID from annotations\n const slackId = entity.metadata?.annotations?.[ANNOTATION_SLACK_BOT_NOTIFY];\n if (slackId) {\n return slackId;\n }\n\n // If no Slack ID in annotations and entity is a User, try to find by email\n if (isUserEntity(entity)) {\n return this.findSlackIdByEmail(entity);\n }\n\n return undefined;\n }\n\n private async findSlackIdByEmail(\n entity: UserEntity,\n ): Promise<string | undefined> {\n const email = entity.spec?.profile?.email;\n if (!email) {\n return undefined;\n }\n\n try {\n const user = await this.slack.users.lookupByEmail({ email });\n return user.user?.id;\n } catch (error) {\n this.logger.warn(\n `Failed to lookup Slack user by email ${email}: ${error}`,\n );\n return undefined;\n }\n }\n\n async sendNotification(args: ChatPostMessageArguments): Promise<void> {\n const response = await this.slack.chat.postMessage(args);\n\n if (!response.ok) {\n throw new Error(`Failed to send notification: ${response.error}`);\n }\n }\n}\n"],"names":["WebClient","metrics","pThrottle","durationToMilliseconds","parseEntityRef","toChatPostMessageArgs","ANNOTATION_SLACK_BOT_NOTIFY","DataLoader","NotFoundError","isUserEntity"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-notifications-backend-module-slack",
3
- "version": "0.1.0",
3
+ "version": "0.1.1-next.1",
4
4
  "description": "The slack backend module for the notifications plugin.",
5
5
  "backstage": {
6
6
  "role": "backend-plugin-module",
@@ -37,14 +37,14 @@
37
37
  "test": "backstage-cli package test"
38
38
  },
39
39
  "dependencies": {
40
- "@backstage/backend-plugin-api": "^1.3.0",
41
- "@backstage/catalog-client": "^1.9.1",
42
- "@backstage/catalog-model": "^1.7.3",
43
- "@backstage/config": "^1.3.2",
44
- "@backstage/errors": "^1.2.7",
45
- "@backstage/plugin-notifications-common": "^0.0.8",
46
- "@backstage/plugin-notifications-node": "^0.2.14",
47
- "@backstage/types": "^1.2.1",
40
+ "@backstage/backend-plugin-api": "1.3.1-next.1",
41
+ "@backstage/catalog-client": "1.10.0-next.0",
42
+ "@backstage/catalog-model": "1.7.3",
43
+ "@backstage/config": "1.3.2",
44
+ "@backstage/errors": "1.2.7",
45
+ "@backstage/plugin-notifications-common": "0.0.8",
46
+ "@backstage/plugin-notifications-node": "0.2.15-next.1",
47
+ "@backstage/types": "1.2.1",
48
48
  "@opentelemetry/api": "^1.9.0",
49
49
  "@slack/bolt": "^3.21.4",
50
50
  "@slack/types": "^2.14.0",
@@ -53,10 +53,10 @@
53
53
  "p-throttle": "^4.1.1"
54
54
  },
55
55
  "devDependencies": {
56
- "@backstage/backend-test-utils": "^1.4.0",
57
- "@backstage/cli": "^0.32.0",
58
- "@backstage/plugin-catalog-node": "^1.16.3",
59
- "@backstage/test-utils": "^1.7.7",
56
+ "@backstage/backend-test-utils": "1.5.0-next.1",
57
+ "@backstage/cli": "0.32.1-next.1",
58
+ "@backstage/plugin-catalog-node": "1.17.0-next.1",
59
+ "@backstage/test-utils": "1.7.8-next.0",
60
60
  "@faker-js/faker": "^8.4.1",
61
61
  "msw": "^2.0.0"
62
62
  },