@backstage/plugin-notifications-backend-module-slack 0.4.5 → 0.4.6-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,26 @@
1
1
  # @backstage/plugin-notifications-backend-module-slack
2
2
 
3
+ ## 0.4.6-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 80a548a: Added `resolveNotificationLink` utility function that resolves relative notification links to absolute URLs using a provided base URL. This can be used by notification processor modules to ensure links are absolute before forwarding them to external systems.
8
+ - Updated dependencies
9
+ - @backstage/config@1.3.9-next.0
10
+ - @backstage/plugin-notifications-node@0.2.30-next.1
11
+ - @backstage/plugin-catalog-node@2.2.5-next.1
12
+ - @backstage/backend-plugin-api@1.10.1-next.1
13
+ - @backstage/plugin-notifications-common@0.2.4-next.0
14
+
15
+ ## 0.4.6-next.0
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies
20
+ - @backstage/backend-plugin-api@1.10.1-next.0
21
+ - @backstage/plugin-catalog-node@2.2.5-next.0
22
+ - @backstage/plugin-notifications-node@0.2.30-next.0
23
+
3
24
  ## 0.4.5
4
25
 
5
26
  ### Patch Changes
package/dist/index.cjs.js CHANGED
@@ -10,6 +10,7 @@ require('@slack/web-api');
10
10
  require('dataloader');
11
11
  require('p-throttle');
12
12
  var constants = require('./lib/constants.cjs.js');
13
+ require('@backstage/plugin-notifications-node');
13
14
  var module$1 = require('./module.cjs.js');
14
15
  var extensions = require('./extensions.cjs.js');
15
16
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;"}
@@ -25,9 +25,10 @@ class SlackNotificationProcessor {
25
25
  messagesFailed;
26
26
  messagesUpdated;
27
27
  db;
28
+ frontendBaseUrl;
29
+ entityLoader;
28
30
  broadcastChannels;
29
31
  broadcastRoutes;
30
- entityLoader;
31
32
  username;
32
33
  concurrencyLimit;
33
34
  throttleInterval;
@@ -47,6 +48,7 @@ class SlackNotificationProcessor {
47
48
  const throttleInterval = c.has("throttleInterval") ? types.durationToMilliseconds(
48
49
  config.readDurationFromConfig(c, { key: "throttleInterval" })
49
50
  ) : types.durationToMilliseconds({ minutes: 1 });
51
+ const frontendBaseUrl = config$1.getString("app.baseUrl");
50
52
  return new SlackNotificationProcessor({
51
53
  slack,
52
54
  broadcastChannels,
@@ -54,6 +56,7 @@ class SlackNotificationProcessor {
54
56
  username,
55
57
  concurrencyLimit,
56
58
  throttleInterval,
59
+ frontendBaseUrl,
57
60
  ...options
58
61
  });
59
62
  });
@@ -64,6 +67,7 @@ class SlackNotificationProcessor {
64
67
  catalog,
65
68
  logger,
66
69
  metrics,
70
+ frontendBaseUrl,
67
71
  slack,
68
72
  broadcastChannels,
69
73
  broadcastRoutes,
@@ -75,6 +79,7 @@ class SlackNotificationProcessor {
75
79
  this.logger = logger;
76
80
  this.catalog = catalog;
77
81
  this.auth = auth;
82
+ this.frontendBaseUrl = frontendBaseUrl;
78
83
  this.slack = slack;
79
84
  this.broadcastChannels = broadcastChannels;
80
85
  this.broadcastRoutes = broadcastRoutes;
@@ -169,6 +174,9 @@ class SlackNotificationProcessor {
169
174
  }
170
175
  const entityRefs = [options.recipients.entityRef].flat();
171
176
  const metadataChannel = options.payload.metadata?.slackChannel;
177
+ const formattedPayload = await this.formatPayloadDescriptionForSlack(
178
+ options.payload
179
+ );
172
180
  const outbound = [];
173
181
  await Promise.all(
174
182
  entityRefs.map(async (entityRef) => {
@@ -198,7 +206,8 @@ class SlackNotificationProcessor {
198
206
  );
199
207
  const payload = util.toChatPostMessageArgs({
200
208
  channel,
201
- payload: options.payload,
209
+ payload: formattedPayload,
210
+ frontendBaseUrl: this.frontendBaseUrl,
202
211
  username: this.username,
203
212
  blockKitRenderer: this.blockKitRenderer
204
213
  });
@@ -246,6 +255,7 @@ class SlackNotificationProcessor {
246
255
  (channel) => util.toChatPostMessageArgs({
247
256
  channel,
248
257
  payload: formattedPayload,
258
+ frontendBaseUrl: this.frontendBaseUrl,
249
259
  username: this.username,
250
260
  blockKitRenderer: this.blockKitRenderer
251
261
  })
@@ -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 { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport {\n MetricsService,\n MetricsServiceCounter,\n} from '@backstage/backend-plugin-api/alpha';\nimport {\n Entity,\n isUserEntity,\n parseEntityRef,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { NotFoundError, toError } 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 {\n ChatPostMessageArguments,\n ChatUpdateArguments,\n WebClient,\n} from '@slack/web-api';\nimport DataLoader from 'dataloader';\nimport { Knex } from 'knex';\nimport pThrottle from 'p-throttle';\nimport { ANNOTATION_SLACK_BOT_NOTIFY } from './constants';\nimport { BroadcastRoute } from './types';\nimport { ExpiryMap, toChatPostMessageArgs } from './util';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { SlackBlockKitRenderer } from '../extensions';\n\ninterface ScopeContext {\n origin: string;\n scope?: string;\n isUpdate?: boolean;\n}\n\nexport class SlackNotificationProcessor implements NotificationProcessor {\n private readonly logger: LoggerService;\n private readonly catalog: CatalogService;\n private readonly auth: AuthService;\n private readonly slack: WebClient;\n private readonly sendNotifications: (\n opts: ChatPostMessageArguments[],\n scopeContext?: ScopeContext,\n ) => Promise<void>;\n private readonly messagesSent: MetricsServiceCounter;\n private readonly messagesFailed: MetricsServiceCounter;\n private readonly messagesUpdated: MetricsServiceCounter;\n private db?: Knex;\n private readonly broadcastChannels?: string[];\n private readonly broadcastRoutes?: BroadcastRoute[];\n private readonly entityLoader: DataLoader<string, Entity | undefined>;\n private readonly username?: string;\n private readonly concurrencyLimit: number;\n private readonly throttleInterval: number;\n private readonly blockKitRenderer?: SlackBlockKitRenderer;\n\n static fromConfig(\n config: Config,\n options: {\n auth: AuthService;\n logger: LoggerService;\n catalog: CatalogService;\n metrics: MetricsService;\n slack?: WebClient;\n broadcastChannels?: string[];\n blockKitRenderer?: SlackBlockKitRenderer;\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 const username = c.getOptionalString('username');\n const broadcastRoutesConfig = c.getOptionalConfigArray('broadcastRoutes');\n const broadcastRoutes = broadcastRoutesConfig?.map(route =>\n this.parseBroadcastRoute(route),\n );\n const concurrencyLimit = c.getOptionalNumber('concurrencyLimit') ?? 10;\n const throttleInterval = c.has('throttleInterval')\n ? durationToMilliseconds(\n readDurationFromConfig(c, { key: 'throttleInterval' }),\n )\n : durationToMilliseconds({ minutes: 1 });\n return new SlackNotificationProcessor({\n slack,\n broadcastChannels,\n broadcastRoutes,\n username,\n concurrencyLimit,\n throttleInterval,\n ...options,\n });\n });\n }\n\n private constructor(options: {\n slack: WebClient;\n auth: AuthService;\n logger: LoggerService;\n catalog: CatalogService;\n metrics: MetricsService;\n broadcastChannels?: string[];\n broadcastRoutes?: BroadcastRoute[];\n username?: string;\n concurrencyLimit?: number;\n throttleInterval?: number;\n blockKitRenderer?: SlackBlockKitRenderer;\n }) {\n const {\n auth,\n catalog,\n logger,\n metrics,\n slack,\n broadcastChannels,\n broadcastRoutes,\n username,\n concurrencyLimit,\n throttleInterval,\n blockKitRenderer,\n } = options;\n this.logger = logger;\n this.catalog = catalog;\n this.auth = auth;\n this.slack = slack;\n this.broadcastChannels = broadcastChannels;\n this.broadcastRoutes = broadcastRoutes;\n this.username = username;\n this.concurrencyLimit = concurrencyLimit ?? 10;\n this.throttleInterval =\n throttleInterval ?? durationToMilliseconds({ minutes: 1 });\n this.blockKitRenderer = blockKitRenderer;\n\n this.entityLoader = new DataLoader<string, Entity | undefined>(\n async entityRefs => {\n return await this.catalog\n .getEntitiesByRefs(\n {\n entityRefs: entityRefs.slice(),\n fields: [\n `kind`,\n `spec.profile.email`,\n `metadata.annotations.${ANNOTATION_SLACK_BOT_NOTIFY}`,\n ],\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n )\n .then(r => r.items);\n },\n {\n name: 'SlackNotificationProcessor.entityLoader',\n cacheMap: new ExpiryMap(durationToMilliseconds({ minutes: 10 })),\n maxBatchSize: 100,\n batchScheduleFn: cb =>\n setTimeout(cb, durationToMilliseconds({ milliseconds: 10 })),\n },\n );\n\n this.messagesSent = metrics.createCounter(\n 'notifications.processors.slack.sent.count',\n {\n description: 'Number of messages sent to Slack successfully',\n unit: '{message}',\n },\n );\n this.messagesFailed = metrics.createCounter(\n 'notifications.processors.slack.error.count',\n {\n description: 'Number of messages that failed to send to Slack',\n unit: '{message}',\n },\n );\n this.messagesUpdated = metrics.createCounter(\n 'notifications.processors.slack.update.count',\n {\n description:\n 'Number of existing Slack messages updated via scope matching',\n unit: '{message}',\n },\n );\n\n const throttle = pThrottle({\n limit: this.concurrencyLimit,\n interval: this.throttleInterval,\n });\n const throttled = throttle(\n (opts: ChatPostMessageArguments, ctx?: ScopeContext) =>\n this.sendNotification(opts, ctx),\n );\n this.sendNotifications = async (\n opts: ChatPostMessageArguments[],\n scopeContext?: ScopeContext,\n ) => {\n const results = await Promise.allSettled(\n opts.map(message => throttled(message, scopeContext)),\n );\n\n let sentCount = 0;\n let updateCount = 0;\n let failureCount = 0;\n\n results.forEach((result, index) => {\n if (result.status === 'fulfilled') {\n if (result.value === 'updated') {\n updateCount++;\n } else {\n sentCount++;\n }\n } else {\n this.logger.error(\n `Failed to send Slack channel notification to ${opts[index].channel}: ${result.reason.message}`,\n );\n failureCount++;\n }\n });\n\n this.messagesSent.add(sentCount);\n this.messagesUpdated.add(updateCount);\n this.messagesFailed.add(failureCount);\n };\n }\n\n setDatabase(db: Knex): void {\n this.db = db;\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 const metadataChannel = options.payload.metadata?.slackChannel as\n | string\n | undefined;\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 = metadataChannel;\n if (!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 toError(error).message\n }`,\n );\n return;\n }\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 username: this.username,\n blockKitRenderer: this.blockKitRenderer,\n });\n\n this.logger.debug(\n `Sending Slack channel notification: ${JSON.stringify(payload)}`,\n );\n outbound.push(payload);\n }),\n );\n\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 const routedChannels = this.getBroadcastDestinations(notification);\n destinations.push(...routedChannels);\n } else if (options.recipients.type === 'entity') {\n // Handle user-specific notification\n const entityRefs = [options.recipients.entityRef].flat();\n const explicitUserEntityRefs = entityRefs\n .filter(entityRef => parseEntityRef(entityRef).kind === 'user')\n .map(entityRef => stringifyEntityRef(parseEntityRef(entityRef)));\n const normalizedUserRef = stringifyEntityRef(\n parseEntityRef(notification.user),\n );\n\n if (!explicitUserEntityRefs.includes(normalizedUserRef)) {\n // This user was resolved from a non-user entity. Skip sending 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 formattedPayload = await this.formatPayloadDescriptionForSlack(\n options.payload,\n );\n const outbound = destinations.map(channel =>\n toChatPostMessageArgs({\n channel,\n payload: formattedPayload,\n username: this.username,\n blockKitRenderer: this.blockKitRenderer,\n }),\n );\n\n // Log debug info\n outbound.forEach(payload => {\n this.logger.debug(`Sending notification: ${JSON.stringify(payload)}`);\n });\n\n await this.sendNotifications(outbound, {\n origin: notification.origin,\n scope: notification.payload.scope,\n isUpdate: !!notification.updated,\n });\n }\n\n private async formatPayloadDescriptionForSlack(\n payload: Notification['payload'],\n ) {\n return {\n ...payload,\n description: await this.replaceUserRefsWithSlackIds(payload.description),\n };\n }\n\n async replaceUserRefsWithSlackIds(\n text?: string,\n ): Promise<string | undefined> {\n if (!text) return undefined;\n\n // Match user entity refs like \"<@user:default/billy>\"\n const userRefRegex = /<@(user:[^>]+)>/gi;\n const matches = [...text.matchAll(userRefRegex)];\n\n if (matches.length === 0) return text;\n\n const uniqueUserRefs = new Set(\n matches.map(match => match[1].toLowerCase()),\n );\n\n const slackIdMap = new Map<string, string>();\n\n await Promise.all(\n [...uniqueUserRefs].map(async userRef => {\n try {\n const slackId = await this.getSlackNotificationTarget(userRef);\n if (slackId) {\n slackIdMap.set(userRef, `<@${slackId}>`);\n }\n } catch (error) {\n this.logger.warn(\n `Failed to resolve Slack ID for user ref \"${userRef}\": ${error}`,\n );\n }\n }),\n );\n\n return text.replace(userRefRegex, (match, userRef) => {\n const slackId = slackIdMap.get(userRef.toLowerCase());\n return slackId ?? match;\n });\n }\n\n async getSlackNotificationTarget(\n entityRef: string,\n ): Promise<string | undefined> {\n const entity = await this.entityLoader.load(entityRef);\n if (!entity) {\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(\n args: ChatPostMessageArguments,\n scopeContext?: ScopeContext,\n ): Promise<'sent' | 'updated'> {\n const channel = args.channel as string;\n const scope = scopeContext?.scope;\n\n // If this is a scoped update, try to update the existing Slack message.\n const origin = scopeContext?.origin;\n if (scopeContext?.isUpdate && origin && scope && this.db) {\n const storedTs = await this.getStoredTimestamp(origin, scope, channel);\n if (storedTs) {\n const updateArgs = {\n channel,\n ts: storedTs,\n ...('text' in args ? { text: args.text } : {}),\n ...('blocks' in args ? { blocks: args.blocks } : {}),\n ...('attachments' in args ? { attachments: args.attachments } : {}),\n } as ChatUpdateArguments;\n const updateResponse = await this.slack.chat.update(updateArgs);\n\n if (!updateResponse.ok) {\n throw new Error(\n `Failed to update notification: ${updateResponse.error}`,\n );\n }\n\n return 'updated';\n }\n }\n\n // Send a new message.\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 // Persist the message timestamp for future scope-based updates.\n if (origin && scope && response.ts && this.db) {\n await this.saveTimestamp(origin, scope, channel, response.ts);\n }\n\n return 'sent';\n }\n\n private async getStoredTimestamp(\n origin: string,\n scope: string,\n channel: string,\n ): Promise<string | undefined> {\n try {\n const row = await this.db!('slack_message_timestamps')\n .where({ origin, scope, channel })\n .first();\n return row?.ts;\n } catch (error) {\n this.logger.warn('Failed to look up stored Slack message timestamp', {\n origin,\n scope,\n channel,\n error,\n });\n return undefined;\n }\n }\n\n private async saveTimestamp(\n origin: string,\n scope: string,\n channel: string,\n ts: string,\n ): Promise<void> {\n try {\n const now = this.db!.fn.now();\n await this.db!('slack_message_timestamps')\n .insert({ origin, scope, channel, ts, created_at: now })\n .onConflict(['origin', 'scope', 'channel'])\n .merge({ ts, created_at: now });\n } catch (error) {\n this.logger.warn('Failed to persist Slack message timestamp', {\n origin,\n scope,\n channel,\n error,\n });\n }\n }\n\n private static parseBroadcastRoute(route: Config): BroadcastRoute {\n const channelValue = route.getOptional('channel');\n let channels: string[];\n\n if (typeof channelValue === 'string') {\n channels = [channelValue];\n } else if (Array.isArray(channelValue)) {\n channels = channelValue as string[];\n } else {\n throw new Error(\n 'broadcastRoutes entry must have a channel property (string or string[])',\n );\n }\n\n return {\n origin: route.getOptionalString('origin'),\n topic: route.getOptionalString('topic'),\n channels,\n };\n }\n\n /**\n * Gets the destination channels for a broadcast notification based on\n * configured routes. Routes are matched by origin and/or topic.\n *\n * Matching precedence:\n * 1. Routes with both origin AND topic matching (most specific)\n * 2. Routes with only origin matching\n * 3. Routes with only topic matching\n * 4. Default broadcastChannels (least specific fallback)\n *\n * The first matching route wins within each precedence level.\n */\n private getBroadcastDestinations(notification: Notification): string[] {\n const { origin } = notification;\n const { topic } = notification.payload;\n\n if (!this.broadcastRoutes || this.broadcastRoutes.length === 0) {\n // Fall back to legacy broadcastChannels config\n return this.broadcastChannels ?? [];\n }\n\n // Find most specific match\n // Priority 1: origin AND topic match\n const originAndTopicMatch = this.broadcastRoutes.find(\n route =>\n route.origin !== undefined &&\n route.topic !== undefined &&\n route.origin === origin &&\n route.topic === topic,\n );\n\n if (originAndTopicMatch) {\n return originAndTopicMatch.channels;\n }\n\n // Priority 2: origin-only match (no topic specified in route)\n const originOnlyMatch = this.broadcastRoutes.find(\n route =>\n route.origin !== undefined &&\n route.topic === undefined &&\n route.origin === origin,\n );\n\n if (originOnlyMatch) {\n return originOnlyMatch.channels;\n }\n\n // Priority 3: topic-only match (no origin specified in route)\n const topicOnlyMatch = this.broadcastRoutes.find(\n route =>\n route.topic !== undefined &&\n route.origin === undefined &&\n route.topic === topic,\n );\n\n if (topicOnlyMatch) {\n return topicOnlyMatch.channels;\n }\n\n // No match found, fall back to legacy broadcastChannels\n return this.broadcastChannels ?? [];\n }\n}\n"],"names":["config","WebClient","durationToMilliseconds","readDurationFromConfig","DataLoader","ANNOTATION_SLACK_BOT_NOTIFY","ExpiryMap","pThrottle","parseEntityRef","toError","toChatPostMessageArgs","stringifyEntityRef","NotFoundError","isUserEntity"],"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 { AuthService, LoggerService } from '@backstage/backend-plugin-api';\nimport {\n MetricsService,\n MetricsServiceCounter,\n} from '@backstage/backend-plugin-api/alpha';\nimport {\n Entity,\n isUserEntity,\n parseEntityRef,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { NotFoundError, toError } 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 {\n ChatPostMessageArguments,\n ChatUpdateArguments,\n WebClient,\n} from '@slack/web-api';\nimport DataLoader from 'dataloader';\nimport { Knex } from 'knex';\nimport pThrottle from 'p-throttle';\nimport { ANNOTATION_SLACK_BOT_NOTIFY } from './constants';\nimport { BroadcastRoute } from './types';\nimport { ExpiryMap, toChatPostMessageArgs } from './util';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { SlackBlockKitRenderer } from '../extensions';\n\ninterface ScopeContext {\n origin: string;\n scope?: string;\n isUpdate?: boolean;\n}\n\nexport class SlackNotificationProcessor implements NotificationProcessor {\n private readonly logger: LoggerService;\n private readonly catalog: CatalogService;\n private readonly auth: AuthService;\n private readonly slack: WebClient;\n private readonly sendNotifications: (\n opts: ChatPostMessageArguments[],\n scopeContext?: ScopeContext,\n ) => Promise<void>;\n private readonly messagesSent: MetricsServiceCounter;\n private readonly messagesFailed: MetricsServiceCounter;\n private readonly messagesUpdated: MetricsServiceCounter;\n private db?: Knex;\n private readonly frontendBaseUrl: string;\n private readonly entityLoader: DataLoader<string, Entity | undefined>;\n private readonly broadcastChannels?: string[];\n private readonly broadcastRoutes?: BroadcastRoute[];\n private readonly username?: string;\n private readonly concurrencyLimit: number;\n private readonly throttleInterval: number;\n private readonly blockKitRenderer?: SlackBlockKitRenderer;\n\n static fromConfig(\n config: Config,\n options: {\n auth: AuthService;\n logger: LoggerService;\n catalog: CatalogService;\n metrics: MetricsService;\n slack?: WebClient;\n broadcastChannels?: string[];\n blockKitRenderer?: SlackBlockKitRenderer;\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 const username = c.getOptionalString('username');\n const broadcastRoutesConfig = c.getOptionalConfigArray('broadcastRoutes');\n const broadcastRoutes = broadcastRoutesConfig?.map(route =>\n this.parseBroadcastRoute(route),\n );\n const concurrencyLimit = c.getOptionalNumber('concurrencyLimit') ?? 10;\n const throttleInterval = c.has('throttleInterval')\n ? durationToMilliseconds(\n readDurationFromConfig(c, { key: 'throttleInterval' }),\n )\n : durationToMilliseconds({ minutes: 1 });\n const frontendBaseUrl = config.getString('app.baseUrl');\n return new SlackNotificationProcessor({\n slack,\n broadcastChannels,\n broadcastRoutes,\n username,\n concurrencyLimit,\n throttleInterval,\n frontendBaseUrl,\n ...options,\n });\n });\n }\n\n private constructor(options: {\n slack: WebClient;\n auth: AuthService;\n logger: LoggerService;\n catalog: CatalogService;\n metrics: MetricsService;\n frontendBaseUrl: string;\n broadcastChannels?: string[];\n broadcastRoutes?: BroadcastRoute[];\n username?: string;\n concurrencyLimit?: number;\n throttleInterval?: number;\n blockKitRenderer?: SlackBlockKitRenderer;\n }) {\n const {\n auth,\n catalog,\n logger,\n metrics,\n frontendBaseUrl,\n slack,\n broadcastChannels,\n broadcastRoutes,\n username,\n concurrencyLimit,\n throttleInterval,\n blockKitRenderer,\n } = options;\n this.logger = logger;\n this.catalog = catalog;\n this.auth = auth;\n this.frontendBaseUrl = frontendBaseUrl;\n this.slack = slack;\n this.broadcastChannels = broadcastChannels;\n this.broadcastRoutes = broadcastRoutes;\n this.username = username;\n this.concurrencyLimit = concurrencyLimit ?? 10;\n this.throttleInterval =\n throttleInterval ?? durationToMilliseconds({ minutes: 1 });\n this.blockKitRenderer = blockKitRenderer;\n\n this.entityLoader = new DataLoader<string, Entity | undefined>(\n async entityRefs => {\n return await this.catalog\n .getEntitiesByRefs(\n {\n entityRefs: entityRefs.slice(),\n fields: [\n `kind`,\n `spec.profile.email`,\n `metadata.annotations.${ANNOTATION_SLACK_BOT_NOTIFY}`,\n ],\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n )\n .then(r => r.items);\n },\n {\n name: 'SlackNotificationProcessor.entityLoader',\n cacheMap: new ExpiryMap(durationToMilliseconds({ minutes: 10 })),\n maxBatchSize: 100,\n batchScheduleFn: cb =>\n setTimeout(cb, durationToMilliseconds({ milliseconds: 10 })),\n },\n );\n\n this.messagesSent = metrics.createCounter(\n 'notifications.processors.slack.sent.count',\n {\n description: 'Number of messages sent to Slack successfully',\n unit: '{message}',\n },\n );\n this.messagesFailed = metrics.createCounter(\n 'notifications.processors.slack.error.count',\n {\n description: 'Number of messages that failed to send to Slack',\n unit: '{message}',\n },\n );\n this.messagesUpdated = metrics.createCounter(\n 'notifications.processors.slack.update.count',\n {\n description:\n 'Number of existing Slack messages updated via scope matching',\n unit: '{message}',\n },\n );\n\n const throttle = pThrottle({\n limit: this.concurrencyLimit,\n interval: this.throttleInterval,\n });\n const throttled = throttle(\n (opts: ChatPostMessageArguments, ctx?: ScopeContext) =>\n this.sendNotification(opts, ctx),\n );\n this.sendNotifications = async (\n opts: ChatPostMessageArguments[],\n scopeContext?: ScopeContext,\n ) => {\n const results = await Promise.allSettled(\n opts.map(message => throttled(message, scopeContext)),\n );\n\n let sentCount = 0;\n let updateCount = 0;\n let failureCount = 0;\n\n results.forEach((result, index) => {\n if (result.status === 'fulfilled') {\n if (result.value === 'updated') {\n updateCount++;\n } else {\n sentCount++;\n }\n } else {\n this.logger.error(\n `Failed to send Slack channel notification to ${opts[index].channel}: ${result.reason.message}`,\n );\n failureCount++;\n }\n });\n\n this.messagesSent.add(sentCount);\n this.messagesUpdated.add(updateCount);\n this.messagesFailed.add(failureCount);\n };\n }\n\n setDatabase(db: Knex): void {\n this.db = db;\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 const metadataChannel = options.payload.metadata?.slackChannel as\n | string\n | undefined;\n const formattedPayload = await this.formatPayloadDescriptionForSlack(\n options.payload,\n );\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 = metadataChannel;\n if (!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 toError(error).message\n }`,\n );\n return;\n }\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: formattedPayload,\n frontendBaseUrl: this.frontendBaseUrl,\n username: this.username,\n blockKitRenderer: this.blockKitRenderer,\n });\n\n this.logger.debug(\n `Sending Slack channel notification: ${JSON.stringify(payload)}`,\n );\n outbound.push(payload);\n }),\n );\n\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 const routedChannels = this.getBroadcastDestinations(notification);\n destinations.push(...routedChannels);\n } else if (options.recipients.type === 'entity') {\n // Handle user-specific notification\n const entityRefs = [options.recipients.entityRef].flat();\n const explicitUserEntityRefs = entityRefs\n .filter(entityRef => parseEntityRef(entityRef).kind === 'user')\n .map(entityRef => stringifyEntityRef(parseEntityRef(entityRef)));\n const normalizedUserRef = stringifyEntityRef(\n parseEntityRef(notification.user),\n );\n\n if (!explicitUserEntityRefs.includes(normalizedUserRef)) {\n // This user was resolved from a non-user entity. Skip sending 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 formattedPayload = await this.formatPayloadDescriptionForSlack(\n options.payload,\n );\n const outbound = destinations.map(channel =>\n toChatPostMessageArgs({\n channel,\n payload: formattedPayload,\n frontendBaseUrl: this.frontendBaseUrl,\n username: this.username,\n blockKitRenderer: this.blockKitRenderer,\n }),\n );\n\n // Log debug info\n outbound.forEach(payload => {\n this.logger.debug(`Sending notification: ${JSON.stringify(payload)}`);\n });\n\n await this.sendNotifications(outbound, {\n origin: notification.origin,\n scope: notification.payload.scope,\n isUpdate: !!notification.updated,\n });\n }\n\n private async formatPayloadDescriptionForSlack(\n payload: Notification['payload'],\n ) {\n return {\n ...payload,\n description: await this.replaceUserRefsWithSlackIds(payload.description),\n };\n }\n\n async replaceUserRefsWithSlackIds(\n text?: string,\n ): Promise<string | undefined> {\n if (!text) return undefined;\n\n // Match user entity refs like \"<@user:default/billy>\"\n const userRefRegex = /<@(user:[^>]+)>/gi;\n const matches = [...text.matchAll(userRefRegex)];\n\n if (matches.length === 0) return text;\n\n const uniqueUserRefs = new Set(\n matches.map(match => match[1].toLowerCase()),\n );\n\n const slackIdMap = new Map<string, string>();\n\n await Promise.all(\n [...uniqueUserRefs].map(async userRef => {\n try {\n const slackId = await this.getSlackNotificationTarget(userRef);\n if (slackId) {\n slackIdMap.set(userRef, `<@${slackId}>`);\n }\n } catch (error) {\n this.logger.warn(\n `Failed to resolve Slack ID for user ref \"${userRef}\": ${error}`,\n );\n }\n }),\n );\n\n return text.replace(userRefRegex, (match, userRef) => {\n const slackId = slackIdMap.get(userRef.toLowerCase());\n return slackId ?? match;\n });\n }\n\n async getSlackNotificationTarget(\n entityRef: string,\n ): Promise<string | undefined> {\n const entity = await this.entityLoader.load(entityRef);\n if (!entity) {\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(\n args: ChatPostMessageArguments,\n scopeContext?: ScopeContext,\n ): Promise<'sent' | 'updated'> {\n const channel = args.channel as string;\n const scope = scopeContext?.scope;\n\n // If this is a scoped update, try to update the existing Slack message.\n const origin = scopeContext?.origin;\n if (scopeContext?.isUpdate && origin && scope && this.db) {\n const storedTs = await this.getStoredTimestamp(origin, scope, channel);\n if (storedTs) {\n const updateArgs = {\n channel,\n ts: storedTs,\n ...('text' in args ? { text: args.text } : {}),\n ...('blocks' in args ? { blocks: args.blocks } : {}),\n ...('attachments' in args ? { attachments: args.attachments } : {}),\n } as ChatUpdateArguments;\n const updateResponse = await this.slack.chat.update(updateArgs);\n\n if (!updateResponse.ok) {\n throw new Error(\n `Failed to update notification: ${updateResponse.error}`,\n );\n }\n\n return 'updated';\n }\n }\n\n // Send a new message.\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 // Persist the message timestamp for future scope-based updates.\n if (origin && scope && response.ts && this.db) {\n await this.saveTimestamp(origin, scope, channel, response.ts);\n }\n\n return 'sent';\n }\n\n private async getStoredTimestamp(\n origin: string,\n scope: string,\n channel: string,\n ): Promise<string | undefined> {\n try {\n const row = await this.db!('slack_message_timestamps')\n .where({ origin, scope, channel })\n .first();\n return row?.ts;\n } catch (error) {\n this.logger.warn('Failed to look up stored Slack message timestamp', {\n origin,\n scope,\n channel,\n error,\n });\n return undefined;\n }\n }\n\n private async saveTimestamp(\n origin: string,\n scope: string,\n channel: string,\n ts: string,\n ): Promise<void> {\n try {\n const now = this.db!.fn.now();\n await this.db!('slack_message_timestamps')\n .insert({ origin, scope, channel, ts, created_at: now })\n .onConflict(['origin', 'scope', 'channel'])\n .merge({ ts, created_at: now });\n } catch (error) {\n this.logger.warn('Failed to persist Slack message timestamp', {\n origin,\n scope,\n channel,\n error,\n });\n }\n }\n\n private static parseBroadcastRoute(route: Config): BroadcastRoute {\n const channelValue = route.getOptional('channel');\n let channels: string[];\n\n if (typeof channelValue === 'string') {\n channels = [channelValue];\n } else if (Array.isArray(channelValue)) {\n channels = channelValue as string[];\n } else {\n throw new Error(\n 'broadcastRoutes entry must have a channel property (string or string[])',\n );\n }\n\n return {\n origin: route.getOptionalString('origin'),\n topic: route.getOptionalString('topic'),\n channels,\n };\n }\n\n /**\n * Gets the destination channels for a broadcast notification based on\n * configured routes. Routes are matched by origin and/or topic.\n *\n * Matching precedence:\n * 1. Routes with both origin AND topic matching (most specific)\n * 2. Routes with only origin matching\n * 3. Routes with only topic matching\n * 4. Default broadcastChannels (least specific fallback)\n *\n * The first matching route wins within each precedence level.\n */\n private getBroadcastDestinations(notification: Notification): string[] {\n const { origin } = notification;\n const { topic } = notification.payload;\n\n if (!this.broadcastRoutes || this.broadcastRoutes.length === 0) {\n // Fall back to legacy broadcastChannels config\n return this.broadcastChannels ?? [];\n }\n\n // Find most specific match\n // Priority 1: origin AND topic match\n const originAndTopicMatch = this.broadcastRoutes.find(\n route =>\n route.origin !== undefined &&\n route.topic !== undefined &&\n route.origin === origin &&\n route.topic === topic,\n );\n\n if (originAndTopicMatch) {\n return originAndTopicMatch.channels;\n }\n\n // Priority 2: origin-only match (no topic specified in route)\n const originOnlyMatch = this.broadcastRoutes.find(\n route =>\n route.origin !== undefined &&\n route.topic === undefined &&\n route.origin === origin,\n );\n\n if (originOnlyMatch) {\n return originOnlyMatch.channels;\n }\n\n // Priority 3: topic-only match (no origin specified in route)\n const topicOnlyMatch = this.broadcastRoutes.find(\n route =>\n route.topic !== undefined &&\n route.origin === undefined &&\n route.topic === topic,\n );\n\n if (topicOnlyMatch) {\n return topicOnlyMatch.channels;\n }\n\n // No match found, fall back to legacy broadcastChannels\n return this.broadcastChannels ?? [];\n }\n}\n"],"names":["config","WebClient","durationToMilliseconds","readDurationFromConfig","DataLoader","ANNOTATION_SLACK_BOT_NOTIFY","ExpiryMap","pThrottle","parseEntityRef","toError","toChatPostMessageArgs","stringifyEntityRef","NotFoundError","isUserEntity"],"mappings":"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"}
@@ -1,8 +1,14 @@
1
1
  'use strict';
2
2
 
3
+ var pluginNotificationsNode = require('@backstage/plugin-notifications-node');
4
+
3
5
  function toChatPostMessageArgs(options) {
4
- const { channel, payload, username, blockKitRenderer } = options;
5
- const blocks = (blockKitRenderer ?? toSlackBlockKit)(payload);
6
+ const { channel, payload, frontendBaseUrl, username, blockKitRenderer } = options;
7
+ const normalizedPayload = {
8
+ ...payload,
9
+ link: pluginNotificationsNode.resolveNotificationLink(payload.link, frontendBaseUrl)
10
+ };
11
+ const blocks = (blockKitRenderer ?? toSlackBlockKit)(normalizedPayload);
6
12
  const args = {
7
13
  channel,
8
14
  text: payload.title,
@@ -1 +1 @@
1
- {"version":3,"file":"util.cjs.js","sources":["../../src/lib/util.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 NotificationPayload,\n NotificationSeverity,\n} from '@backstage/plugin-notifications-common';\nimport { ChatPostMessageArguments, KnownBlock } from '@slack/web-api';\nimport { SlackBlockKitRenderer } from '../extensions';\n\nexport function toChatPostMessageArgs(options: {\n channel: string;\n payload: NotificationPayload;\n username?: string;\n blockKitRenderer?: SlackBlockKitRenderer;\n}): ChatPostMessageArguments {\n const { channel, payload, username, blockKitRenderer } = options;\n const blocks = (blockKitRenderer ?? toSlackBlockKit)(payload);\n\n const args: ChatPostMessageArguments = {\n channel,\n text: payload.title,\n username,\n attachments: [\n {\n color: getColor(payload.severity),\n blocks,\n fallback: payload.title,\n },\n ],\n };\n\n return args;\n}\n\nexport function toSlackBlockKit(payload: NotificationPayload): KnownBlock[] {\n const { description, link, severity, topic } = payload;\n return [\n {\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: description ?? 'No description provided',\n },\n accessory: {\n type: 'button',\n text: {\n type: 'plain_text',\n text: 'View More',\n },\n ...(link && { url: link }),\n action_id: 'button-action',\n },\n },\n {\n type: 'context',\n elements: [\n {\n type: 'plain_text',\n text: `Severity: ${severity ?? 'normal'}`,\n emoji: true,\n },\n {\n type: 'plain_text',\n text: `Topic: ${topic ?? 'N/A'}`,\n emoji: true,\n },\n ],\n },\n ];\n}\n\nfunction getColor(severity: NotificationSeverity | undefined) {\n switch (severity) {\n case 'critical':\n return '#FF0000'; // Red\n case 'high':\n return '#FFA500'; // Orange\n case 'low':\n case 'normal':\n default:\n return '#00A699'; // Neutral color\n }\n}\n\n// Simple expiry map for the data loader, which only expects a map that implements set, get, and delete and clear\nexport class ExpiryMap<K, V> extends Map<K, V> {\n #ttlMs: number;\n #timestamps: Map<K, number> = new Map();\n\n constructor(ttlMs: number) {\n super();\n this.#ttlMs = ttlMs;\n }\n\n set(key: K, value: V) {\n const result = super.set(key, value);\n this.#timestamps.set(key, Date.now());\n return result;\n }\n\n get(key: K) {\n if (!this.has(key)) {\n return undefined;\n }\n const timestamp = this.#timestamps.get(key)!;\n if (Date.now() - timestamp > this.#ttlMs) {\n this.delete(key);\n return undefined;\n }\n return super.get(key);\n }\n\n delete(key: K) {\n this.#timestamps.delete(key);\n return super.delete(key);\n }\n\n clear() {\n this.#timestamps.clear();\n return super.clear();\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"util.cjs.js","sources":["../../src/lib/util.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 NotificationPayload,\n NotificationSeverity,\n} from '@backstage/plugin-notifications-common';\nimport { resolveNotificationLink } from '@backstage/plugin-notifications-node';\nimport { ChatPostMessageArguments, KnownBlock } from '@slack/web-api';\nimport { SlackBlockKitRenderer } from '../extensions';\n\nexport function toChatPostMessageArgs(options: {\n channel: string;\n payload: NotificationPayload;\n frontendBaseUrl: string;\n username?: string;\n blockKitRenderer?: SlackBlockKitRenderer;\n}): ChatPostMessageArguments {\n const { channel, payload, frontendBaseUrl, username, blockKitRenderer } =\n options;\n const normalizedPayload = {\n ...payload,\n link: resolveNotificationLink(payload.link, frontendBaseUrl),\n };\n const blocks = (blockKitRenderer ?? toSlackBlockKit)(normalizedPayload);\n\n const args: ChatPostMessageArguments = {\n channel,\n text: payload.title,\n username,\n attachments: [\n {\n color: getColor(payload.severity),\n blocks,\n fallback: payload.title,\n },\n ],\n };\n\n return args;\n}\n\nexport function toSlackBlockKit(payload: NotificationPayload): KnownBlock[] {\n const { description, link, severity, topic } = payload;\n return [\n {\n type: 'section',\n text: {\n type: 'mrkdwn',\n text: description ?? 'No description provided',\n },\n accessory: {\n type: 'button',\n text: {\n type: 'plain_text',\n text: 'View More',\n },\n ...(link && { url: link }),\n action_id: 'button-action',\n },\n },\n {\n type: 'context',\n elements: [\n {\n type: 'plain_text',\n text: `Severity: ${severity ?? 'normal'}`,\n emoji: true,\n },\n {\n type: 'plain_text',\n text: `Topic: ${topic ?? 'N/A'}`,\n emoji: true,\n },\n ],\n },\n ];\n}\n\nfunction getColor(severity: NotificationSeverity | undefined) {\n switch (severity) {\n case 'critical':\n return '#FF0000'; // Red\n case 'high':\n return '#FFA500'; // Orange\n case 'low':\n case 'normal':\n default:\n return '#00A699'; // Neutral color\n }\n}\n\n// Simple expiry map for the data loader, which only expects a map that implements set, get, and delete and clear\nexport class ExpiryMap<K, V> extends Map<K, V> {\n #ttlMs: number;\n #timestamps: Map<K, number> = new Map();\n\n constructor(ttlMs: number) {\n super();\n this.#ttlMs = ttlMs;\n }\n\n set(key: K, value: V) {\n const result = super.set(key, value);\n this.#timestamps.set(key, Date.now());\n return result;\n }\n\n get(key: K) {\n if (!this.has(key)) {\n return undefined;\n }\n const timestamp = this.#timestamps.get(key)!;\n if (Date.now() - timestamp > this.#ttlMs) {\n this.delete(key);\n return undefined;\n }\n return super.get(key);\n }\n\n delete(key: K) {\n this.#timestamps.delete(key);\n return super.delete(key);\n }\n\n clear() {\n this.#timestamps.clear();\n return super.clear();\n }\n}\n"],"names":["resolveNotificationLink"],"mappings":";;;;AAwBO,SAAS,sBAAsB,OAAA,EAMT;AAC3B,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAS,eAAA,EAAiB,QAAA,EAAU,kBAAiB,GACpE,OAAA;AACF,EAAA,MAAM,iBAAA,GAAoB;AAAA,IACxB,GAAG,OAAA;AAAA,IACH,IAAA,EAAMA,+CAAA,CAAwB,OAAA,CAAQ,IAAA,EAAM,eAAe;AAAA,GAC7D;AACA,EAAA,MAAM,MAAA,GAAA,CAAU,gBAAA,IAAoB,eAAA,EAAiB,iBAAiB,CAAA;AAEtE,EAAA,MAAM,IAAA,GAAiC;AAAA,IACrC,OAAA;AAAA,IACA,MAAM,OAAA,CAAQ,KAAA;AAAA,IACd,QAAA;AAAA,IACA,WAAA,EAAa;AAAA,MACX;AAAA,QACE,KAAA,EAAO,QAAA,CAAS,OAAA,CAAQ,QAAQ,CAAA;AAAA,QAChC,MAAA;AAAA,QACA,UAAU,OAAA,CAAQ;AAAA;AACpB;AACF,GACF;AAEA,EAAA,OAAO,IAAA;AACT;AAEO,SAAS,gBAAgB,OAAA,EAA4C;AAC1E,EAAA,MAAM,EAAE,WAAA,EAAa,IAAA,EAAM,QAAA,EAAU,OAAM,GAAI,OAAA;AAC/C,EAAA,OAAO;AAAA,IACL;AAAA,MACE,IAAA,EAAM,SAAA;AAAA,MACN,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,MAAM,WAAA,IAAe;AAAA,OACvB;AAAA,MACA,SAAA,EAAW;AAAA,QACT,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM;AAAA,UACJ,IAAA,EAAM,YAAA;AAAA,UACN,IAAA,EAAM;AAAA,SACR;AAAA,QACA,GAAI,IAAA,IAAQ,EAAE,GAAA,EAAK,IAAA,EAAK;AAAA,QACxB,SAAA,EAAW;AAAA;AACb,KACF;AAAA,IACA;AAAA,MACE,IAAA,EAAM,SAAA;AAAA,MACN,QAAA,EAAU;AAAA,QACR;AAAA,UACE,IAAA,EAAM,YAAA;AAAA,UACN,IAAA,EAAM,CAAA,UAAA,EAAa,QAAA,IAAY,QAAQ,CAAA,CAAA;AAAA,UACvC,KAAA,EAAO;AAAA,SACT;AAAA,QACA;AAAA,UACE,IAAA,EAAM,YAAA;AAAA,UACN,IAAA,EAAM,CAAA,OAAA,EAAU,KAAA,IAAS,KAAK,CAAA,CAAA;AAAA,UAC9B,KAAA,EAAO;AAAA;AACT;AACF;AACF,GACF;AACF;AAEA,SAAS,SAAS,QAAA,EAA4C;AAC5D,EAAA,QAAQ,QAAA;AAAU,IAChB,KAAK,UAAA;AACH,MAAA,OAAO,SAAA;AAAA;AAAA,IACT,KAAK,MAAA;AACH,MAAA,OAAO,SAAA;AAAA;AAAA,IACT,KAAK,KAAA;AAAA,IACL,KAAK,QAAA;AAAA,IACL;AACE,MAAA,OAAO,SAAA;AAAA;AAEb;AAGO,MAAM,kBAAwB,GAAA,CAAU;AAAA,EAC7C,MAAA;AAAA,EACA,WAAA,uBAAkC,GAAA,EAAI;AAAA,EAEtC,YAAY,KAAA,EAAe;AACzB,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,MAAA,GAAS,KAAA;AAAA,EAChB;AAAA,EAEA,GAAA,CAAI,KAAQ,KAAA,EAAU;AACpB,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACnC,IAAA,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAA,EAAK,IAAA,CAAK,KAAK,CAAA;AACpC,IAAA,OAAO,MAAA;AAAA,EACT;AAAA,EAEA,IAAI,GAAA,EAAQ;AACV,IAAA,IAAI,CAAC,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA,EAAG;AAClB,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA;AAC1C,IAAA,IAAI,IAAA,CAAK,GAAA,EAAI,GAAI,SAAA,GAAY,KAAK,MAAA,EAAQ;AACxC,MAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AACf,MAAA,OAAO,MAAA;AAAA,IACT;AACA,IAAA,OAAO,KAAA,CAAM,IAAI,GAAG,CAAA;AAAA,EACtB;AAAA,EAEA,OAAO,GAAA,EAAQ;AACb,IAAA,IAAA,CAAK,WAAA,CAAY,OAAO,GAAG,CAAA;AAC3B,IAAA,OAAO,KAAA,CAAM,OAAO,GAAG,CAAA;AAAA,EACzB;AAAA,EAEA,KAAA,GAAQ;AACN,IAAA,IAAA,CAAK,YAAY,KAAA,EAAM;AACvB,IAAA,OAAO,MAAM,KAAA,EAAM;AAAA,EACrB;AACF;;;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-notifications-backend-module-slack",
3
- "version": "0.4.5",
3
+ "version": "0.4.6-next.1",
4
4
  "description": "The slack backend module for the notifications plugin.",
5
5
  "backstage": {
6
6
  "role": "backend-plugin-module",
@@ -38,14 +38,14 @@
38
38
  "test": "backstage-cli package test"
39
39
  },
40
40
  "dependencies": {
41
- "@backstage/backend-plugin-api": "^1.10.0",
42
- "@backstage/catalog-model": "^1.10.0",
43
- "@backstage/config": "^1.3.8",
44
- "@backstage/errors": "^1.3.1",
45
- "@backstage/plugin-catalog-node": "^2.2.4",
46
- "@backstage/plugin-notifications-common": "^0.2.3",
47
- "@backstage/plugin-notifications-node": "^0.2.29",
48
- "@backstage/types": "^1.2.2",
41
+ "@backstage/backend-plugin-api": "1.10.1-next.1",
42
+ "@backstage/catalog-model": "1.10.0",
43
+ "@backstage/config": "1.3.9-next.0",
44
+ "@backstage/errors": "1.3.1",
45
+ "@backstage/plugin-catalog-node": "2.2.5-next.1",
46
+ "@backstage/plugin-notifications-common": "0.2.4-next.0",
47
+ "@backstage/plugin-notifications-node": "0.2.30-next.1",
48
+ "@backstage/types": "1.2.2",
49
49
  "@slack/bolt": "^3.21.4",
50
50
  "@slack/types": "^2.14.0",
51
51
  "@slack/web-api": "^7.5.0",
@@ -54,9 +54,9 @@
54
54
  "p-throttle": "^4.1.1"
55
55
  },
56
56
  "devDependencies": {
57
- "@backstage/backend-test-utils": "^1.11.6",
58
- "@backstage/cli": "^0.36.5",
59
- "@backstage/test-utils": "^1.7.21",
57
+ "@backstage/backend-test-utils": "1.11.7-next.1",
58
+ "@backstage/cli": "0.36.6-next.1",
59
+ "@backstage/test-utils": "1.7.22-next.2",
60
60
  "@faker-js/faker": "^10.0.0"
61
61
  },
62
62
  "configSchema": "config.schema.json",