@backstage/plugin-notifications-backend 0.5.10-next.0 → 0.5.10

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,36 @@
1
1
  # @backstage/plugin-notifications-backend
2
2
 
3
+ ## 0.5.10
4
+
5
+ ### Patch Changes
6
+
7
+ - a95cebd: Internal refactoring for better type support
8
+ - 7e7ed57: A new extension point was added that can be used to modify how the users receiving notifications
9
+ are resolved. The interface passed to the extension point should only return complete user entity references
10
+ based on the notification target references and the excluded entity references. Note that the inputs are lists
11
+ of entity references that can be any entity kind, not just user entities.
12
+
13
+ Using this extension point will override the default behavior of resolving users with the
14
+ `DefaultNotificationRecipientResolver`.
15
+
16
+ - Updated dependencies
17
+ - @backstage/plugin-catalog-node@1.19.0
18
+ - @backstage/plugin-auth-node@0.6.7
19
+ - @backstage/plugin-events-node@0.4.15
20
+ - @backstage/types@1.2.2
21
+ - @backstage/plugin-notifications-node@0.2.19
22
+ - @backstage/backend-plugin-api@1.4.3
23
+ - @backstage/plugin-signals-node@0.1.24
24
+
25
+ ## 0.5.10-next.1
26
+
27
+ ### Patch Changes
28
+
29
+ - Updated dependencies
30
+ - @backstage/plugin-auth-node@0.6.7-next.1
31
+ - @backstage/plugin-catalog-node@1.19.0-next.1
32
+ - @backstage/plugin-notifications-node@0.2.19-next.1
33
+
3
34
  ## 0.5.10-next.0
4
35
 
5
36
  ### Patch Changes
@@ -10,12 +10,24 @@ var NotificationCleaner = require('./service/NotificationCleaner.cjs.js');
10
10
 
11
11
  class NotificationsProcessingExtensionPointImpl {
12
12
  #processors = new Array();
13
+ #recipientResolver = void 0;
13
14
  addProcessor(...processors) {
14
15
  this.#processors.push(...processors.flat());
15
16
  }
16
17
  get processors() {
17
18
  return this.#processors;
18
19
  }
20
+ setNotificationRecipientResolver(resolver) {
21
+ if (this.#recipientResolver) {
22
+ throw new Error(
23
+ "Notification recipient resolver is already set. You can only set it once."
24
+ );
25
+ }
26
+ this.#recipientResolver = resolver;
27
+ }
28
+ get recipientResolver() {
29
+ return this.#recipientResolver;
30
+ }
19
31
  }
20
32
  const notificationsPlugin = backendPluginApi.createBackendPlugin({
21
33
  pluginId: "notifications",
@@ -61,7 +73,8 @@ const notificationsPlugin = backendPluginApi.createBackendPlugin({
61
73
  store,
62
74
  catalog,
63
75
  signals,
64
- processors: processingExtensions.processors
76
+ processors: processingExtensions.processors,
77
+ recipientResolver: processingExtensions.recipientResolver
65
78
  })
66
79
  );
67
80
  httpRouter.addAuthPolicy({
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\nimport { signalsServiceRef } from '@backstage/plugin-signals-node';\nimport {\n NotificationProcessor,\n notificationsProcessingExtensionPoint,\n NotificationsProcessingExtensionPoint,\n} from '@backstage/plugin-notifications-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport { DatabaseNotificationsStore } from './database';\nimport { NotificationCleaner } from './service/NotificationCleaner.ts';\n\nclass NotificationsProcessingExtensionPointImpl\n implements NotificationsProcessingExtensionPoint\n{\n #processors = new Array<NotificationProcessor>();\n\n addProcessor(\n ...processors: Array<NotificationProcessor | Array<NotificationProcessor>>\n ): void {\n this.#processors.push(...processors.flat());\n }\n\n get processors() {\n return this.#processors;\n }\n}\n\n/**\n * Notifications backend plugin\n *\n * @public\n */\nexport const notificationsPlugin = createBackendPlugin({\n pluginId: 'notifications',\n register(env) {\n const processingExtensions =\n new NotificationsProcessingExtensionPointImpl();\n env.registerExtensionPoint(\n notificationsProcessingExtensionPoint,\n processingExtensions,\n );\n\n env.registerInit({\n deps: {\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n database: coreServices.database,\n signals: signalsServiceRef,\n config: coreServices.rootConfig,\n catalog: catalogServiceRef,\n scheduler: coreServices.scheduler,\n },\n async init({\n auth,\n httpAuth,\n userInfo,\n httpRouter,\n logger,\n database,\n signals,\n config,\n catalog,\n scheduler,\n }) {\n const store = await DatabaseNotificationsStore.create({ database });\n\n httpRouter.use(\n await createRouter({\n auth,\n httpAuth,\n userInfo,\n logger,\n config,\n store,\n catalog,\n signals,\n processors: processingExtensions.processors,\n }),\n );\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n\n const cleaner = new NotificationCleaner(\n config,\n scheduler,\n logger,\n store,\n );\n await cleaner.initTaskRunner();\n },\n });\n },\n});\n"],"names":["createBackendPlugin","notificationsProcessingExtensionPoint","coreServices","signalsServiceRef","catalogServiceRef","DatabaseNotificationsStore","createRouter","NotificationCleaner"],"mappings":";;;;;;;;;;AA+BA,MAAM,yCAAA,CAEN;AAAA,EACE,WAAA,GAAc,IAAI,KAAA,EAA6B;AAAA,EAE/C,gBACK,UAAA,EACG;AACN,IAAA,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,GAAG,UAAA,CAAW,MAAM,CAAA;AAAA,EAC5C;AAAA,EAEA,IAAI,UAAA,GAAa;AACf,IAAA,OAAO,IAAA,CAAK,WAAA;AAAA,EACd;AACF;AAOO,MAAM,sBAAsBA,oCAAA,CAAoB;AAAA,EACrD,QAAA,EAAU,eAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,oBAAA,GACJ,IAAI,yCAAA,EAA0C;AAChD,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,6DAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,MAAMC,6BAAA,CAAa,IAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,YAAYA,6BAAA,CAAa,UAAA;AAAA,QACzB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,OAAA,EAASC,mCAAA;AAAA,QACT,QAAQD,6BAAA,CAAa,UAAA;AAAA,QACrB,OAAA,EAASE,mCAAA;AAAA,QACT,WAAWF,6BAAA,CAAa;AAAA,OAC1B;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,IAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAA;AAAA,QACA,OAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAM,QAAQ,MAAMG,qDAAA,CAA2B,MAAA,CAAO,EAAE,UAAU,CAAA;AAElE,QAAA,UAAA,CAAW,GAAA;AAAA,UACT,MAAMC,mBAAA,CAAa;AAAA,YACjB,IAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,YACA,MAAA;AAAA,YACA,MAAA;AAAA,YACA,KAAA;AAAA,YACA,OAAA;AAAA,YACA,OAAA;AAAA,YACA,YAAY,oBAAA,CAAqB;AAAA,WAClC;AAAA,SACH;AACA,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAED,QAAA,MAAM,UAAU,IAAIC,uCAAA;AAAA,UAClB,MAAA;AAAA,UACA,SAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,MAAM,QAAQ,cAAA,EAAe;AAAA,MAC/B;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 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 coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\nimport { signalsServiceRef } from '@backstage/plugin-signals-node';\nimport {\n NotificationProcessor,\n NotificationRecipientResolver,\n notificationsProcessingExtensionPoint,\n NotificationsProcessingExtensionPoint,\n} from '@backstage/plugin-notifications-node';\nimport { catalogServiceRef } from '@backstage/plugin-catalog-node';\nimport { DatabaseNotificationsStore } from './database';\nimport { NotificationCleaner } from './service/NotificationCleaner.ts';\n\nclass NotificationsProcessingExtensionPointImpl\n implements NotificationsProcessingExtensionPoint\n{\n #processors = new Array<NotificationProcessor>();\n #recipientResolver: NotificationRecipientResolver | undefined = undefined;\n\n addProcessor(\n ...processors: Array<NotificationProcessor | Array<NotificationProcessor>>\n ): void {\n this.#processors.push(...processors.flat());\n }\n\n get processors() {\n return this.#processors;\n }\n\n setNotificationRecipientResolver(\n resolver: NotificationRecipientResolver,\n ): void {\n if (this.#recipientResolver) {\n throw new Error(\n 'Notification recipient resolver is already set. You can only set it once.',\n );\n }\n this.#recipientResolver = resolver;\n }\n\n get recipientResolver() {\n return this.#recipientResolver;\n }\n}\n\n/**\n * Notifications backend plugin\n *\n * @public\n */\nexport const notificationsPlugin = createBackendPlugin({\n pluginId: 'notifications',\n register(env) {\n const processingExtensions =\n new NotificationsProcessingExtensionPointImpl();\n env.registerExtensionPoint(\n notificationsProcessingExtensionPoint,\n processingExtensions,\n );\n\n env.registerInit({\n deps: {\n auth: coreServices.auth,\n httpAuth: coreServices.httpAuth,\n userInfo: coreServices.userInfo,\n httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n database: coreServices.database,\n signals: signalsServiceRef,\n config: coreServices.rootConfig,\n catalog: catalogServiceRef,\n scheduler: coreServices.scheduler,\n },\n async init({\n auth,\n httpAuth,\n userInfo,\n httpRouter,\n logger,\n database,\n signals,\n config,\n catalog,\n scheduler,\n }) {\n const store = await DatabaseNotificationsStore.create({ database });\n\n httpRouter.use(\n await createRouter({\n auth,\n httpAuth,\n userInfo,\n logger,\n config,\n store,\n catalog,\n signals,\n processors: processingExtensions.processors,\n recipientResolver: processingExtensions.recipientResolver,\n }),\n );\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n\n const cleaner = new NotificationCleaner(\n config,\n scheduler,\n logger,\n store,\n );\n await cleaner.initTaskRunner();\n },\n });\n },\n});\n"],"names":["createBackendPlugin","notificationsProcessingExtensionPoint","coreServices","signalsServiceRef","catalogServiceRef","DatabaseNotificationsStore","createRouter","NotificationCleaner"],"mappings":";;;;;;;;;;AAgCA,MAAM,yCAAA,CAEN;AAAA,EACE,WAAA,GAAc,IAAI,KAAA,EAA6B;AAAA,EAC/C,kBAAA,GAAgE,MAAA;AAAA,EAEhE,gBACK,UAAA,EACG;AACN,IAAA,IAAA,CAAK,WAAA,CAAY,IAAA,CAAK,GAAG,UAAA,CAAW,MAAM,CAAA;AAAA,EAC5C;AAAA,EAEA,IAAI,UAAA,GAAa;AACf,IAAA,OAAO,IAAA,CAAK,WAAA;AAAA,EACd;AAAA,EAEA,iCACE,QAAA,EACM;AACN,IAAA,IAAI,KAAK,kBAAA,EAAoB;AAC3B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,IAAA,CAAK,kBAAA,GAAqB,QAAA;AAAA,EAC5B;AAAA,EAEA,IAAI,iBAAA,GAAoB;AACtB,IAAA,OAAO,IAAA,CAAK,kBAAA;AAAA,EACd;AACF;AAOO,MAAM,sBAAsBA,oCAAA,CAAoB;AAAA,EACrD,QAAA,EAAU,eAAA;AAAA,EACV,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,oBAAA,GACJ,IAAI,yCAAA,EAA0C;AAChD,IAAA,GAAA,CAAI,sBAAA;AAAA,MACFC,6DAAA;AAAA,MACA;AAAA,KACF;AAEA,IAAA,GAAA,CAAI,YAAA,CAAa;AAAA,MACf,IAAA,EAAM;AAAA,QACJ,MAAMC,6BAAA,CAAa,IAAA;AAAA,QACnB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,YAAYA,6BAAA,CAAa,UAAA;AAAA,QACzB,QAAQA,6BAAA,CAAa,MAAA;AAAA,QACrB,UAAUA,6BAAA,CAAa,QAAA;AAAA,QACvB,OAAA,EAASC,mCAAA;AAAA,QACT,QAAQD,6BAAA,CAAa,UAAA;AAAA,QACrB,OAAA,EAASE,mCAAA;AAAA,QACT,WAAWF,6BAAA,CAAa;AAAA,OAC1B;AAAA,MACA,MAAM,IAAA,CAAK;AAAA,QACT,IAAA;AAAA,QACA,QAAA;AAAA,QACA,QAAA;AAAA,QACA,UAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAA;AAAA,QACA,OAAA;AAAA,QACA;AAAA,OACF,EAAG;AACD,QAAA,MAAM,QAAQ,MAAMG,qDAAA,CAA2B,MAAA,CAAO,EAAE,UAAU,CAAA;AAElE,QAAA,UAAA,CAAW,GAAA;AAAA,UACT,MAAMC,mBAAA,CAAa;AAAA,YACjB,IAAA;AAAA,YACA,QAAA;AAAA,YACA,QAAA;AAAA,YACA,MAAA;AAAA,YACA,MAAA;AAAA,YACA,KAAA;AAAA,YACA,OAAA;AAAA,YACA,OAAA;AAAA,YACA,YAAY,oBAAA,CAAqB,UAAA;AAAA,YACjC,mBAAmB,oBAAA,CAAqB;AAAA,WACzC;AAAA,SACH;AACA,QAAA,UAAA,CAAW,aAAA,CAAc;AAAA,UACvB,IAAA,EAAM,SAAA;AAAA,UACN,KAAA,EAAO;AAAA,SACR,CAAA;AAED,QAAA,MAAM,UAAU,IAAIC,uCAAA;AAAA,UAClB,MAAA;AAAA,UACA,SAAA;AAAA,UACA,MAAA;AAAA,UACA;AAAA,SACF;AACA,QAAA,MAAM,QAAQ,cAAA,EAAe;AAAA,MAC/B;AAAA,KACD,CAAA;AAAA,EACH;AACF,CAAC;;;;"}
@@ -0,0 +1,116 @@
1
+ 'use strict';
2
+
3
+ var catalogModel = require('@backstage/catalog-model');
4
+
5
+ const isUserEntityRef = (ref) => catalogModel.parseEntityRef(ref).kind.toLocaleLowerCase() === "user";
6
+ const partitionEntityRefs = (refs) => {
7
+ const ret = [[], []];
8
+ for (const ref of refs) {
9
+ if (isUserEntityRef(ref)) {
10
+ ret[0].push(ref);
11
+ } else {
12
+ ret[1].push(ref);
13
+ }
14
+ }
15
+ return ret;
16
+ };
17
+ class DefaultNotificationRecipientResolver {
18
+ constructor(auth, catalog) {
19
+ this.auth = auth;
20
+ this.catalog = catalog;
21
+ }
22
+ async resolveNotificationRecipients(options) {
23
+ const { entityRefs, excludeEntityRefs = [] } = options;
24
+ const [userEntityRefs, otherEntityRefs] = partitionEntityRefs(entityRefs);
25
+ const users = userEntityRefs.filter(
26
+ (ref) => !excludeEntityRefs.includes(ref)
27
+ );
28
+ const filtered = otherEntityRefs.filter(
29
+ (ref) => !excludeEntityRefs.includes(ref)
30
+ );
31
+ const fields = ["kind", "metadata.name", "metadata.namespace", "relations"];
32
+ let entities = [];
33
+ if (filtered.length > 0) {
34
+ const fetchedEntities = await this.catalog.getEntitiesByRefs(
35
+ {
36
+ entityRefs: filtered,
37
+ fields
38
+ },
39
+ { credentials: await this.auth.getOwnServiceCredentials() }
40
+ );
41
+ entities = fetchedEntities.items;
42
+ }
43
+ const cachedEntityRefs = /* @__PURE__ */ new Map();
44
+ const mapEntity = async (entity) => {
45
+ if (!entity) {
46
+ return [];
47
+ }
48
+ const currentEntityRef = catalogModel.stringifyEntityRef(entity);
49
+ if (excludeEntityRefs.includes(currentEntityRef)) {
50
+ return [];
51
+ }
52
+ if (cachedEntityRefs.has(currentEntityRef)) {
53
+ return cachedEntityRefs.get(currentEntityRef);
54
+ }
55
+ if (catalogModel.isUserEntity(entity)) {
56
+ return [currentEntityRef];
57
+ }
58
+ if (catalogModel.isGroupEntity(entity)) {
59
+ if (!entity.relations?.length) {
60
+ return [];
61
+ }
62
+ const groupUsers = entity.relations.filter(
63
+ (relation) => relation.type === catalogModel.RELATION_HAS_MEMBER && isUserEntityRef(relation.targetRef)
64
+ ).map((r) => r.targetRef);
65
+ const childGroupRefs = entity.relations.filter((relation) => relation.type === catalogModel.RELATION_PARENT_OF).map((r) => r.targetRef);
66
+ let childGroupUsers = [];
67
+ if (childGroupRefs.length > 0) {
68
+ const childGroups = await this.catalog.getEntitiesByRefs(
69
+ {
70
+ entityRefs: childGroupRefs,
71
+ fields
72
+ },
73
+ { credentials: await this.auth.getOwnServiceCredentials() }
74
+ );
75
+ childGroupUsers = await Promise.all(childGroups.items.map(mapEntity));
76
+ }
77
+ const ret = [
78
+ .../* @__PURE__ */ new Set([...groupUsers, ...childGroupUsers.flat(2)])
79
+ ].filter((ref) => !excludeEntityRefs.includes(ref));
80
+ cachedEntityRefs.set(currentEntityRef, ret);
81
+ return ret;
82
+ }
83
+ if (entity.relations?.length) {
84
+ const ownerRef = entity.relations.find(
85
+ (relation) => relation.type === catalogModel.RELATION_OWNED_BY
86
+ )?.targetRef;
87
+ if (!ownerRef) {
88
+ return [];
89
+ }
90
+ if (isUserEntityRef(ownerRef)) {
91
+ if (excludeEntityRefs.includes(ownerRef)) {
92
+ return [];
93
+ }
94
+ return [ownerRef];
95
+ }
96
+ const owner = await this.catalog.getEntityByRef(ownerRef, {
97
+ credentials: await this.auth.getOwnServiceCredentials()
98
+ });
99
+ const ret = await mapEntity(owner);
100
+ cachedEntityRefs.set(currentEntityRef, ret);
101
+ return ret;
102
+ }
103
+ return [];
104
+ };
105
+ for (const entity of entities) {
106
+ const u = await mapEntity(entity);
107
+ users.push(...u);
108
+ }
109
+ return {
110
+ userEntityRefs: [...new Set(users)].filter(Boolean).filter((ref) => !excludeEntityRefs.includes(ref))
111
+ };
112
+ }
113
+ }
114
+
115
+ exports.DefaultNotificationRecipientResolver = DefaultNotificationRecipientResolver;
116
+ //# sourceMappingURL=DefaultNotificationRecipientResolver.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultNotificationRecipientResolver.cjs.js","sources":["../../src/service/DefaultNotificationRecipientResolver.ts"],"sourcesContent":["/*\n * Copyright 2024 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 Entity,\n isGroupEntity,\n isUserEntity,\n parseEntityRef,\n RELATION_HAS_MEMBER,\n RELATION_OWNED_BY,\n RELATION_PARENT_OF,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { AuthService } from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport { NotificationRecipientResolver } from '@backstage/plugin-notifications-node';\n\nconst isUserEntityRef = (ref: string) =>\n parseEntityRef(ref).kind.toLocaleLowerCase() === 'user';\n\n// Partitions array of entity references to two arrays; user entity refs and other entity refs\nconst partitionEntityRefs = (refs: string[]): string[][] => {\n const ret = [[], []] as string[][];\n for (const ref of refs) {\n if (isUserEntityRef(ref)) {\n ret[0].push(ref);\n } else {\n ret[1].push(ref);\n }\n }\n return ret;\n};\n\nexport class DefaultNotificationRecipientResolver\n implements NotificationRecipientResolver\n{\n constructor(\n private readonly auth: AuthService,\n private readonly catalog: CatalogService,\n ) {}\n\n async resolveNotificationRecipients(options: {\n entityRefs: string[];\n excludeEntityRefs?: string[];\n }): Promise<{ userEntityRefs: string[] }> {\n const { entityRefs, excludeEntityRefs = [] } = options;\n\n const [userEntityRefs, otherEntityRefs] = partitionEntityRefs(entityRefs);\n const users: string[] = userEntityRefs.filter(\n ref => !excludeEntityRefs.includes(ref),\n );\n const filtered = otherEntityRefs.filter(\n ref => !excludeEntityRefs.includes(ref),\n );\n\n const fields = ['kind', 'metadata.name', 'metadata.namespace', 'relations'];\n let entities: Array<Entity | undefined> = [];\n if (filtered.length > 0) {\n const fetchedEntities = await this.catalog.getEntitiesByRefs(\n {\n entityRefs: filtered,\n fields,\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n entities = fetchedEntities.items;\n }\n\n const cachedEntityRefs = new Map<string, string[]>();\n\n const mapEntity = async (entity: Entity | undefined): Promise<string[]> => {\n if (!entity) {\n return [];\n }\n\n const currentEntityRef = stringifyEntityRef(entity);\n if (excludeEntityRefs.includes(currentEntityRef)) {\n return [];\n }\n\n if (cachedEntityRefs.has(currentEntityRef)) {\n return cachedEntityRefs.get(currentEntityRef)!;\n }\n\n if (isUserEntity(entity)) {\n return [currentEntityRef];\n }\n\n if (isGroupEntity(entity)) {\n if (!entity.relations?.length) {\n return [];\n }\n\n const groupUsers = entity.relations\n .filter(\n relation =>\n relation.type === RELATION_HAS_MEMBER &&\n isUserEntityRef(relation.targetRef),\n )\n .map(r => r.targetRef);\n\n const childGroupRefs = entity.relations\n .filter(relation => relation.type === RELATION_PARENT_OF)\n .map(r => r.targetRef);\n\n let childGroupUsers: string[][] = [];\n if (childGroupRefs.length > 0) {\n const childGroups = await this.catalog.getEntitiesByRefs(\n {\n entityRefs: childGroupRefs,\n fields,\n },\n { credentials: await this.auth.getOwnServiceCredentials() },\n );\n childGroupUsers = await Promise.all(childGroups.items.map(mapEntity));\n }\n\n const ret = [\n ...new Set([...groupUsers, ...childGroupUsers.flat(2)]),\n ].filter(ref => !excludeEntityRefs.includes(ref));\n cachedEntityRefs.set(currentEntityRef, ret);\n return ret;\n }\n\n if (entity.relations?.length) {\n const ownerRef = entity.relations.find(\n relation => relation.type === RELATION_OWNED_BY,\n )?.targetRef;\n\n if (!ownerRef) {\n return [];\n }\n\n if (isUserEntityRef(ownerRef)) {\n if (excludeEntityRefs.includes(ownerRef)) {\n return [];\n }\n return [ownerRef];\n }\n\n const owner = await this.catalog.getEntityByRef(ownerRef, {\n credentials: await this.auth.getOwnServiceCredentials(),\n });\n const ret = await mapEntity(owner);\n cachedEntityRefs.set(currentEntityRef, ret);\n return ret;\n }\n\n return [];\n };\n\n for (const entity of entities) {\n const u = await mapEntity(entity);\n users.push(...u);\n }\n\n return {\n userEntityRefs: [...new Set(users)]\n .filter(Boolean)\n // Need to filter again after resolving users\n .filter(ref => !excludeEntityRefs.includes(ref)),\n };\n }\n}\n"],"names":["parseEntityRef","stringifyEntityRef","isUserEntity","isGroupEntity","RELATION_HAS_MEMBER","RELATION_PARENT_OF","RELATION_OWNED_BY"],"mappings":"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"}
@@ -7,10 +7,11 @@ var uuid = require('uuid');
7
7
  var errors = require('@backstage/errors');
8
8
  var pluginNotificationsCommon = require('@backstage/plugin-notifications-common');
9
9
  var parseEntityOrderFieldParams = require('./parseEntityOrderFieldParams.cjs.js');
10
- var getUsersForEntityRef = require('./getUsersForEntityRef.cjs.js');
11
10
  var config = require('@backstage/config');
12
11
  var types = require('@backstage/types');
13
12
  var pThrottle = require('p-throttle');
13
+ var catalogModel = require('@backstage/catalog-model');
14
+ var DefaultNotificationRecipientResolver = require('./DefaultNotificationRecipientResolver.cjs.js');
14
15
 
15
16
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
16
17
 
@@ -28,7 +29,8 @@ async function createRouter(options) {
28
29
  userInfo,
29
30
  catalog,
30
31
  processors = [],
31
- signals
32
+ signals,
33
+ recipientResolver
32
34
  } = options;
33
35
  const WEB_NOTIFICATION_CHANNEL = "Web";
34
36
  const frontendBaseUrl = config$1.getString("app.baseUrl");
@@ -43,6 +45,7 @@ async function createRouter(options) {
43
45
  interval: throttleInterval
44
46
  });
45
47
  const defaultNotificationSettings = config$1.getOptional("notifications.defaultSettings");
48
+ const usedRecipientResolver = recipientResolver ?? new DefaultNotificationRecipientResolver.DefaultNotificationRecipientResolver(auth, catalog);
46
49
  const getUser = async (req) => {
47
50
  const credentials = await httpAuth.credentials(req, { allow: ["user"] });
48
51
  const info = await userInfo.getUserInfo(credentials);
@@ -447,9 +450,19 @@ async function createRouter(options) {
447
450
  postProcessNotification(ret, opts);
448
451
  return ret;
449
452
  };
453
+ const filterNonUserEntityRefs = (refs) => {
454
+ return refs.filter((ref) => {
455
+ try {
456
+ const parsed = catalogModel.parseEntityRef(ref);
457
+ return parsed.kind.toLowerCase() === "user";
458
+ } catch {
459
+ return false;
460
+ }
461
+ });
462
+ };
450
463
  const sendUserNotifications = async (baseNotification, users, opts, origin) => {
451
464
  const { scope } = opts.payload;
452
- const uniqueUsers = [...new Set(users)];
465
+ const uniqueUsers = [...new Set(filterNonUserEntityRefs(users))];
453
466
  const throttled = throttle(
454
467
  (user) => sendUserNotification(baseNotification, user, opts, origin, scope)
455
468
  );
@@ -465,7 +478,6 @@ async function createRouter(options) {
465
478
  const { recipients, payload } = opts;
466
479
  const { title, link } = payload;
467
480
  const notifications = [];
468
- let users = [];
469
481
  if (!recipients || !title) {
470
482
  const missing = [
471
483
  !title ? "title" : null,
@@ -499,23 +511,23 @@ async function createRouter(options) {
499
511
  );
500
512
  notifications.push(broadcast);
501
513
  } else if (recipients.type === "entity") {
502
- const entityRef = recipients.entityRef;
514
+ const entityRefs = [recipients.entityRef].flat();
515
+ const excludedEntityRefs = recipients.excludeEntityRef ? [recipients.excludeEntityRef].flat() : void 0;
503
516
  try {
504
- users = await getUsersForEntityRef.getUsersForEntityRef(
505
- entityRef,
506
- recipients.excludeEntityRef ?? [],
507
- { auth, catalog }
517
+ const { userEntityRefs } = await usedRecipientResolver.resolveNotificationRecipients({
518
+ entityRefs,
519
+ excludedEntityRefs
520
+ });
521
+ const userNotifications = await sendUserNotifications(
522
+ baseNotification,
523
+ userEntityRefs,
524
+ opts,
525
+ origin
508
526
  );
527
+ notifications.push(...userNotifications);
509
528
  } catch (e) {
510
- throw new errors.InputError("Failed to resolve notification receivers", e);
529
+ throw new errors.InputError("Failed to send user notifications", e);
511
530
  }
512
- const userNotifications = await sendUserNotifications(
513
- baseNotification,
514
- users,
515
- opts,
516
- origin
517
- );
518
- notifications.push(...userNotifications);
519
531
  } else {
520
532
  throw new errors.InputError(
521
533
  `Invalid recipients type, please use either 'broadcast' or 'entity'`
@@ -1 +1 @@
1
- {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2023 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 express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\nimport {\n normalizeSeverity,\n NotificationGetOptions,\n NotificationsStore,\n TopicGetOptions,\n} from '../database';\nimport { v4 as uuid } from 'uuid';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport {\n NotificationProcessor,\n NotificationSendOptions,\n} from '@backstage/plugin-notifications-node';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n ChannelSetting,\n isNotificationsEnabledFor,\n NewNotificationSignal,\n Notification,\n NotificationReadSignal,\n NotificationSettings,\n notificationSeverities,\n NotificationStatus,\n OriginSetting,\n} from '@backstage/plugin-notifications-common';\nimport { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';\nimport { getUsersForEntityRef } from './getUsersForEntityRef';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\nimport pThrottle from 'p-throttle';\n\n/** @internal */\nexport interface RouterOptions {\n logger: LoggerService;\n config: Config;\n store: NotificationsStore;\n auth: AuthService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n signals?: SignalsService;\n catalog: CatalogService;\n processors?: NotificationProcessor[];\n}\n\n/** @internal */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const {\n config,\n logger,\n store,\n auth,\n httpAuth,\n userInfo,\n catalog,\n processors = [],\n signals,\n } = options;\n\n const WEB_NOTIFICATION_CHANNEL = 'Web';\n const frontendBaseUrl = config.getString('app.baseUrl');\n const concurrencyLimit =\n config.getOptionalNumber('notifications.concurrencyLimit') ?? 10;\n const throttleInterval = config.has('notifications.throttleInterval')\n ? durationToMilliseconds(\n readDurationFromConfig(config, {\n key: 'notifications.throttleInterval',\n }),\n )\n : 50;\n const throttle = pThrottle({\n limit: concurrencyLimit,\n interval: throttleInterval,\n });\n const defaultNotificationSettings: NotificationSettings | undefined =\n config.getOptional<NotificationSettings>('notifications.defaultSettings');\n\n const getUser = async (req: Request<unknown>) => {\n const credentials = await httpAuth.credentials(req, { allow: ['user'] });\n const info = await userInfo.getUserInfo(credentials);\n return info.userEntityRef;\n };\n\n const getNotificationChannels = () => {\n return [WEB_NOTIFICATION_CHANNEL, ...processors.map(p => p.getName())];\n };\n\n const getTopicSettings = (\n topic: any,\n existingOrigin: OriginSetting | undefined,\n defaultOriginSettings: OriginSetting | undefined,\n defaultEnabled: boolean,\n ) => {\n const existingTopic = existingOrigin?.topics?.find(\n t => t.id.toLowerCase() === topic.topic.toLowerCase(),\n );\n const defaultTopicSettings = defaultOriginSettings?.topics?.find(\n t => t.id.toLowerCase() === topic.topic.toLowerCase(),\n );\n\n return {\n id: topic.topic,\n enabled: existingTopic\n ? existingTopic.enabled\n : defaultTopicSettings?.enabled ?? defaultEnabled,\n };\n };\n\n const getOriginSettings = (\n originId: string,\n existingChannel: ChannelSetting | undefined,\n defaultChannelSettings: ChannelSetting | undefined,\n topics: { origin: string; topic: string }[],\n ) => {\n const existingOrigin = existingChannel?.origins?.find(\n o => o.id.toLowerCase() === originId.toLowerCase(),\n );\n\n const defaultOriginSettings = defaultChannelSettings?.origins?.find(\n c => c.id.toLowerCase() === originId.toLowerCase(),\n );\n\n const defaultEnabled = existingOrigin\n ? existingOrigin.enabled\n : defaultOriginSettings?.enabled ?? true;\n\n return {\n id: originId,\n enabled: defaultEnabled,\n topics: topics\n .filter(t => t.origin === originId)\n .map(t =>\n getTopicSettings(\n t,\n existingOrigin,\n defaultOriginSettings,\n defaultEnabled,\n ),\n ),\n };\n };\n\n const getChannelSettings = (\n channelId: string,\n settings: NotificationSettings,\n origins: string[],\n topics: { origin: string; topic: string }[],\n ) => {\n const existingChannel = settings.channels.find(\n c => c.id.toLowerCase() === channelId.toLowerCase(),\n );\n const defaultChannelSettings = defaultNotificationSettings?.channels?.find(\n c => c.id.toLowerCase() === channelId.toLowerCase(),\n );\n\n return {\n id: channelId,\n origins: origins.map(originId =>\n getOriginSettings(\n originId,\n existingChannel,\n defaultChannelSettings,\n topics,\n ),\n ),\n };\n };\n\n const getNotificationSettings = async (\n user: string,\n ): Promise<NotificationSettings> => {\n const { origins } = await store.getUserNotificationOrigins({ user });\n const { topics } = await store.getUserNotificationTopics({ user });\n const settings = await store.getNotificationSettings({ user });\n const channels = getNotificationChannels();\n\n return {\n channels: channels.map(channelId =>\n getChannelSettings(channelId, settings, origins, topics),\n ),\n };\n };\n\n const isNotificationsEnabled = async (opts: {\n user: string;\n channel: string;\n origin: string;\n topic: string | null;\n }) => {\n const settings = await getNotificationSettings(opts.user);\n return isNotificationsEnabledFor(\n settings,\n opts.channel,\n opts.origin,\n opts.topic,\n );\n };\n\n const filterProcessors = async (\n notification:\n | Notification\n | ({ origin: string; user: null } & NotificationSendOptions),\n ) => {\n const result: NotificationProcessor[] = [];\n const { payload, user, origin } = notification;\n\n for (const processor of processors) {\n if (user) {\n const enabled = await isNotificationsEnabled({\n user,\n origin,\n channel: processor.getName(),\n topic: payload.topic ?? null,\n });\n if (!enabled) {\n continue;\n }\n }\n\n if (processor.getNotificationFilters) {\n const filters = processor.getNotificationFilters();\n if (filters.minSeverity) {\n if (\n notificationSeverities.indexOf(payload.severity ?? 'normal') >\n notificationSeverities.indexOf(filters.minSeverity)\n ) {\n continue;\n }\n }\n\n if (filters.maxSeverity) {\n if (\n notificationSeverities.indexOf(payload.severity ?? 'normal') <\n notificationSeverities.indexOf(filters.maxSeverity)\n ) {\n continue;\n }\n }\n\n if (filters.excludedTopics && payload.topic) {\n if (filters.excludedTopics.includes(payload.topic)) {\n continue;\n }\n }\n }\n result.push(processor);\n }\n\n return result;\n };\n\n const processOptions = async (\n opts: NotificationSendOptions,\n origin: string,\n ): Promise<NotificationSendOptions> => {\n const filtered = await filterProcessors({ ...opts, origin, user: null });\n let ret = opts;\n for (const processor of filtered) {\n try {\n ret = processor.processOptions\n ? await processor.processOptions(ret)\n : ret;\n } catch (e) {\n logger.error(\n `Error while processing notification options with ${processor.getName()}: ${e}`,\n );\n }\n }\n return ret;\n };\n\n const preProcessNotification = async (\n notification: Notification,\n opts: NotificationSendOptions,\n ) => {\n const filtered = await filterProcessors(notification);\n let ret = notification;\n for (const processor of filtered) {\n try {\n ret = processor.preProcess\n ? await processor.preProcess(ret, opts)\n : ret;\n } catch (e) {\n logger.error(\n `Error while pre processing notification with ${processor.getName()}: ${e}`,\n );\n }\n }\n return ret;\n };\n\n const postProcessNotification = async (\n notification: Notification,\n opts: NotificationSendOptions,\n ) => {\n const filtered = await filterProcessors(notification);\n for (const processor of filtered) {\n if (processor.postProcess) {\n try {\n await processor.postProcess(notification, opts);\n } catch (e) {\n logger.error(\n `Error while post processing notification with ${processor.getName()}: ${e}`,\n );\n }\n }\n }\n };\n\n const validateLink = (link: string) => {\n const stripLeadingSlash = (s: string) => s.replace(/^\\//, '');\n const ensureTrailingSlash = (s: string) => s.replace(/\\/?$/, '/');\n const url = new URL(\n stripLeadingSlash(link),\n ensureTrailingSlash(frontendBaseUrl),\n );\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new Error('Only HTTP/HTTPS links are allowed');\n }\n };\n\n const appendCommonOptions = (\n req: Request,\n opts: NotificationGetOptions | TopicGetOptions,\n ) => {\n if (req.query.search) {\n opts.search = req.query.search.toString();\n }\n if (req.query.read === 'true') {\n opts.read = true;\n } else if (req.query.read === 'false') {\n opts.read = false;\n // or keep undefined\n }\n\n if (req.query.saved === 'true') {\n opts.saved = true;\n } else if (req.query.saved === 'false') {\n opts.saved = false;\n // or keep undefined\n }\n if (req.query.createdAfter) {\n const sinceEpoch = Date.parse(String(req.query.createdAfter));\n if (isNaN(sinceEpoch)) {\n throw new InputError('Unexpected date format');\n }\n opts.createdAfter = new Date(sinceEpoch);\n }\n if (req.query.minimumSeverity) {\n opts.minimumSeverity = normalizeSeverity(\n req.query.minimumSeverity.toString(),\n );\n }\n };\n\n // TODO: Move to use OpenAPI router instead\n const router = Router();\n router.use(express.json());\n\n const listNotificationsHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const opts: NotificationGetOptions = {\n user: user,\n };\n if (req.query.offset) {\n opts.offset = Number.parseInt(req.query.offset.toString(), 10);\n }\n if (req.query.limit) {\n opts.limit = Number.parseInt(req.query.limit.toString(), 10);\n }\n if (req.query.orderField) {\n opts.orderField = parseEntityOrderFieldParams(req.query);\n }\n\n if (req.query.topic) {\n opts.topic = req.query.topic.toString();\n }\n\n appendCommonOptions(req, opts);\n\n const [notifications, totalCount] = await Promise.all([\n store.getNotifications(opts),\n store.getNotificationsCount(opts),\n ]);\n res.json({\n totalCount,\n notifications,\n });\n };\n\n router.get('/', listNotificationsHandler); // Deprecated endpoint\n router.get('/notifications', listNotificationsHandler);\n\n router.get('/status', async (req: Request<any, NotificationStatus>, res) => {\n const user = await getUser(req);\n const status = await store.getStatus({ user });\n res.json(status);\n });\n\n router.get(\n '/settings',\n async (req: Request<any, NotificationSettings>, res) => {\n const user = await getUser(req);\n const response = await getNotificationSettings(user);\n res.json(response);\n },\n );\n\n router.post(\n '/settings',\n async (\n req: Request<any, NotificationSettings, NotificationSettings>,\n res,\n ) => {\n const user = await getUser(req);\n const channels = getNotificationChannels();\n const settings: NotificationSettings = req.body;\n if (settings.channels.some(c => !channels.includes(c.id))) {\n throw new InputError('Invalid channel');\n }\n await store.saveNotificationSettings({ user, settings });\n const response = await getNotificationSettings(user);\n res.json(response);\n },\n );\n\n const getNotificationHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const opts: NotificationGetOptions = {\n user: user,\n limit: 1,\n ids: [req.params.id],\n };\n const notifications = await store.getNotifications(opts);\n if (notifications.length !== 1) {\n throw new NotFoundError('Not found');\n }\n res.json(notifications[0]);\n };\n\n // Get topics\n const listTopicsHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const opts: TopicGetOptions = {\n user: user,\n };\n\n appendCommonOptions(req, opts);\n\n const topics = await store.getTopics(opts);\n res.json(topics);\n };\n\n router.get('/topics', listTopicsHandler);\n\n // Make sure this is the last \"GET\" handler\n router.get('/:id', getNotificationHandler); // Deprecated endpoint\n router.get('/notifications/:id', getNotificationHandler);\n\n const updateNotificationsHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const { ids, read, saved } = req.body;\n if (!ids || !Array.isArray(ids)) {\n throw new InputError();\n }\n\n if (read === true) {\n await store.markRead({ user, ids });\n\n if (signals) {\n await signals.publish<NotificationReadSignal>({\n recipients: { type: 'user', entityRef: [user] },\n message: { action: 'notification_read', notification_ids: ids },\n channel: 'notifications',\n });\n }\n } else if (read === false) {\n await store.markUnread({ user: user, ids });\n\n if (signals) {\n await signals.publish<NotificationReadSignal>({\n recipients: { type: 'user', entityRef: [user] },\n message: { action: 'notification_unread', notification_ids: ids },\n channel: 'notifications',\n });\n }\n }\n\n if (saved === true) {\n await store.markSaved({ user: user, ids });\n } else if (saved === false) {\n await store.markUnsaved({ user: user, ids });\n }\n\n const notifications = await store.getNotifications({ ids, user: user });\n res.json(notifications);\n };\n\n router.post('/update', updateNotificationsHandler); // Deprecated endpoint\n router.post('/notifications/update', updateNotificationsHandler);\n\n const sendBroadcastNotification = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n opts: NotificationSendOptions,\n origin: string,\n ) => {\n const { scope } = opts.payload;\n const broadcastNotification = {\n ...baseNotification,\n user: null,\n id: uuid(),\n };\n const notification = await preProcessNotification(\n broadcastNotification,\n opts,\n );\n let existingNotification;\n if (scope) {\n existingNotification = await store.getExistingScopeBroadcast({\n scope,\n origin,\n });\n }\n\n let ret = notification;\n if (existingNotification) {\n const restored = await store.restoreExistingNotification({\n id: existingNotification.id,\n notification: { ...notification, user: '' },\n });\n ret = restored ?? notification;\n } else {\n await store.saveBroadcast(notification);\n }\n\n if (signals) {\n await signals.publish<NewNotificationSignal>({\n recipients: { type: 'broadcast' },\n message: {\n action: 'new_notification',\n notification_id: ret.id,\n },\n channel: 'notifications',\n });\n }\n postProcessNotification(ret, opts);\n return notification;\n };\n\n const sendUserNotification = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n user: string,\n opts: NotificationSendOptions,\n origin: string,\n scope?: string,\n ): Promise<Notification | undefined> => {\n const userNotification = {\n ...baseNotification,\n id: uuid(),\n user,\n };\n const notification = await preProcessNotification(userNotification, opts);\n\n const enabled = await isNotificationsEnabled({\n user,\n channel: WEB_NOTIFICATION_CHANNEL,\n origin: userNotification.origin,\n topic: userNotification.payload.topic ?? null,\n });\n\n let ret = notification;\n\n if (!enabled) {\n postProcessNotification(ret, opts);\n return undefined;\n }\n\n let existingNotification;\n if (scope) {\n existingNotification = await store.getExistingScopeNotification({\n user,\n scope,\n origin,\n });\n }\n\n if (existingNotification) {\n const restored = await store.restoreExistingNotification({\n id: existingNotification.id,\n notification,\n });\n ret = restored ?? notification;\n } else {\n await store.saveNotification(notification);\n }\n\n if (signals) {\n await signals.publish<NewNotificationSignal>({\n recipients: { type: 'user', entityRef: [user] },\n message: {\n action: 'new_notification',\n notification_id: ret.id,\n },\n channel: 'notifications',\n });\n }\n postProcessNotification(ret, opts);\n return ret;\n };\n\n const sendUserNotifications = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n users: string[],\n opts: NotificationSendOptions,\n origin: string,\n ): Promise<Notification[]> => {\n const { scope } = opts.payload;\n const uniqueUsers = [...new Set(users)];\n const throttled = throttle((user: string) =>\n sendUserNotification(baseNotification, user, opts, origin, scope),\n );\n const sent = await Promise.all(uniqueUsers.map(user => throttled(user)));\n return sent.filter(n => n !== undefined);\n };\n\n const createNotificationHandler = async (\n req: Request<any, Notification[], NotificationSendOptions>,\n res: Response,\n ) => {\n const credentials = await httpAuth.credentials(req, {\n allow: ['service'],\n });\n\n const origin = credentials.principal.subject;\n const opts = await processOptions(req.body, origin);\n const { recipients, payload } = opts;\n const { title, link } = payload;\n const notifications: Notification[] = [];\n let users = [];\n\n if (!recipients || !title) {\n const missing = [\n !title ? 'title' : null,\n !recipients ? 'recipients' : null,\n ].filter(Boolean);\n const err = `Invalid notification request received: missing ${missing.join(\n ', ',\n )}`;\n throw new InputError(err);\n }\n\n if (link) {\n try {\n validateLink(link);\n } catch (e) {\n throw new InputError('Invalid link provided', e);\n }\n }\n\n const baseNotification = {\n payload: {\n ...payload,\n severity: payload.severity ?? 'normal',\n },\n origin,\n created: new Date(),\n };\n\n if (recipients.type === 'broadcast') {\n const broadcast = await sendBroadcastNotification(\n baseNotification,\n opts,\n origin,\n );\n notifications.push(broadcast);\n } else if (recipients.type === 'entity') {\n const entityRef = recipients.entityRef;\n\n try {\n users = await getUsersForEntityRef(\n entityRef,\n recipients.excludeEntityRef ?? [],\n { auth, catalog },\n );\n } catch (e) {\n throw new InputError('Failed to resolve notification receivers', e);\n }\n\n const userNotifications = await sendUserNotifications(\n baseNotification,\n users,\n opts,\n origin,\n );\n notifications.push(...userNotifications);\n } else {\n throw new InputError(\n `Invalid recipients type, please use either 'broadcast' or 'entity'`,\n );\n }\n\n res.json(notifications);\n };\n\n // Add new notification\n router.post('/', createNotificationHandler);\n router.post('/notifications', createNotificationHandler);\n\n return router;\n}\n"],"names":["config","durationToMilliseconds","readDurationFromConfig","pThrottle","isNotificationsEnabledFor","notificationSeverities","InputError","normalizeSeverity","Router","express","parseEntityOrderFieldParams","NotFoundError","uuid","getUsersForEntityRef"],"mappings":"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"}
1
+ {"version":3,"file":"router.cjs.js","sources":["../../src/service/router.ts"],"sourcesContent":["/*\n * Copyright 2023 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 express, { Request, Response } from 'express';\nimport Router from 'express-promise-router';\nimport {\n normalizeSeverity,\n NotificationGetOptions,\n NotificationsStore,\n TopicGetOptions,\n} from '../database';\nimport { v4 as uuid } from 'uuid';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\nimport {\n NotificationProcessor,\n NotificationRecipientResolver,\n NotificationSendOptions,\n} from '@backstage/plugin-notifications-node';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport {\n AuthService,\n HttpAuthService,\n LoggerService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n ChannelSetting,\n isNotificationsEnabledFor,\n NewNotificationSignal,\n Notification,\n NotificationReadSignal,\n NotificationSettings,\n notificationSeverities,\n NotificationStatus,\n OriginSetting,\n} from '@backstage/plugin-notifications-common';\nimport { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';\nimport { Config, readDurationFromConfig } from '@backstage/config';\nimport { durationToMilliseconds } from '@backstage/types';\nimport pThrottle from 'p-throttle';\nimport { parseEntityRef } from '@backstage/catalog-model';\nimport { DefaultNotificationRecipientResolver } from './DefaultNotificationRecipientResolver.ts';\n\n/** @internal */\nexport interface RouterOptions {\n logger: LoggerService;\n config: Config;\n store: NotificationsStore;\n auth: AuthService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n signals?: SignalsService;\n catalog: CatalogService;\n processors?: NotificationProcessor[];\n recipientResolver?: NotificationRecipientResolver;\n}\n\n/** @internal */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const {\n config,\n logger,\n store,\n auth,\n httpAuth,\n userInfo,\n catalog,\n processors = [],\n signals,\n recipientResolver,\n } = options;\n\n const WEB_NOTIFICATION_CHANNEL = 'Web';\n const frontendBaseUrl = config.getString('app.baseUrl');\n const concurrencyLimit =\n config.getOptionalNumber('notifications.concurrencyLimit') ?? 10;\n const throttleInterval = config.has('notifications.throttleInterval')\n ? durationToMilliseconds(\n readDurationFromConfig(config, {\n key: 'notifications.throttleInterval',\n }),\n )\n : 50;\n const throttle = pThrottle({\n limit: concurrencyLimit,\n interval: throttleInterval,\n });\n const defaultNotificationSettings: NotificationSettings | undefined =\n config.getOptional<NotificationSettings>('notifications.defaultSettings');\n\n const usedRecipientResolver =\n recipientResolver ??\n new DefaultNotificationRecipientResolver(auth, catalog);\n\n const getUser = async (req: Request<unknown>) => {\n const credentials = await httpAuth.credentials(req, { allow: ['user'] });\n const info = await userInfo.getUserInfo(credentials);\n return info.userEntityRef;\n };\n\n const getNotificationChannels = () => {\n return [WEB_NOTIFICATION_CHANNEL, ...processors.map(p => p.getName())];\n };\n\n const getTopicSettings = (\n topic: any,\n existingOrigin: OriginSetting | undefined,\n defaultOriginSettings: OriginSetting | undefined,\n defaultEnabled: boolean,\n ) => {\n const existingTopic = existingOrigin?.topics?.find(\n t => t.id.toLowerCase() === topic.topic.toLowerCase(),\n );\n const defaultTopicSettings = defaultOriginSettings?.topics?.find(\n t => t.id.toLowerCase() === topic.topic.toLowerCase(),\n );\n\n return {\n id: topic.topic,\n enabled: existingTopic\n ? existingTopic.enabled\n : defaultTopicSettings?.enabled ?? defaultEnabled,\n };\n };\n\n const getOriginSettings = (\n originId: string,\n existingChannel: ChannelSetting | undefined,\n defaultChannelSettings: ChannelSetting | undefined,\n topics: { origin: string; topic: string }[],\n ) => {\n const existingOrigin = existingChannel?.origins?.find(\n o => o.id.toLowerCase() === originId.toLowerCase(),\n );\n\n const defaultOriginSettings = defaultChannelSettings?.origins?.find(\n c => c.id.toLowerCase() === originId.toLowerCase(),\n );\n\n const defaultEnabled = existingOrigin\n ? existingOrigin.enabled\n : defaultOriginSettings?.enabled ?? true;\n\n return {\n id: originId,\n enabled: defaultEnabled,\n topics: topics\n .filter(t => t.origin === originId)\n .map(t =>\n getTopicSettings(\n t,\n existingOrigin,\n defaultOriginSettings,\n defaultEnabled,\n ),\n ),\n };\n };\n\n const getChannelSettings = (\n channelId: string,\n settings: NotificationSettings,\n origins: string[],\n topics: { origin: string; topic: string }[],\n ) => {\n const existingChannel = settings.channels.find(\n c => c.id.toLowerCase() === channelId.toLowerCase(),\n );\n const defaultChannelSettings = defaultNotificationSettings?.channels?.find(\n c => c.id.toLowerCase() === channelId.toLowerCase(),\n );\n\n return {\n id: channelId,\n origins: origins.map(originId =>\n getOriginSettings(\n originId,\n existingChannel,\n defaultChannelSettings,\n topics,\n ),\n ),\n };\n };\n\n const getNotificationSettings = async (\n user: string,\n ): Promise<NotificationSettings> => {\n const { origins } = await store.getUserNotificationOrigins({ user });\n const { topics } = await store.getUserNotificationTopics({ user });\n const settings = await store.getNotificationSettings({ user });\n const channels = getNotificationChannels();\n\n return {\n channels: channels.map(channelId =>\n getChannelSettings(channelId, settings, origins, topics),\n ),\n };\n };\n\n const isNotificationsEnabled = async (opts: {\n user: string;\n channel: string;\n origin: string;\n topic: string | null;\n }) => {\n const settings = await getNotificationSettings(opts.user);\n return isNotificationsEnabledFor(\n settings,\n opts.channel,\n opts.origin,\n opts.topic,\n );\n };\n\n const filterProcessors = async (\n notification:\n | Notification\n | ({ origin: string; user: null } & NotificationSendOptions),\n ) => {\n const result: NotificationProcessor[] = [];\n const { payload, user, origin } = notification;\n\n for (const processor of processors) {\n if (user) {\n const enabled = await isNotificationsEnabled({\n user,\n origin,\n channel: processor.getName(),\n topic: payload.topic ?? null,\n });\n if (!enabled) {\n continue;\n }\n }\n\n if (processor.getNotificationFilters) {\n const filters = processor.getNotificationFilters();\n if (filters.minSeverity) {\n if (\n notificationSeverities.indexOf(payload.severity ?? 'normal') >\n notificationSeverities.indexOf(filters.minSeverity)\n ) {\n continue;\n }\n }\n\n if (filters.maxSeverity) {\n if (\n notificationSeverities.indexOf(payload.severity ?? 'normal') <\n notificationSeverities.indexOf(filters.maxSeverity)\n ) {\n continue;\n }\n }\n\n if (filters.excludedTopics && payload.topic) {\n if (filters.excludedTopics.includes(payload.topic)) {\n continue;\n }\n }\n }\n result.push(processor);\n }\n\n return result;\n };\n\n const processOptions = async (\n opts: NotificationSendOptions,\n origin: string,\n ): Promise<NotificationSendOptions> => {\n const filtered = await filterProcessors({ ...opts, origin, user: null });\n let ret = opts;\n for (const processor of filtered) {\n try {\n ret = processor.processOptions\n ? await processor.processOptions(ret)\n : ret;\n } catch (e) {\n logger.error(\n `Error while processing notification options with ${processor.getName()}: ${e}`,\n );\n }\n }\n return ret;\n };\n\n const preProcessNotification = async (\n notification: Notification,\n opts: NotificationSendOptions,\n ) => {\n const filtered = await filterProcessors(notification);\n let ret = notification;\n for (const processor of filtered) {\n try {\n ret = processor.preProcess\n ? await processor.preProcess(ret, opts)\n : ret;\n } catch (e) {\n logger.error(\n `Error while pre processing notification with ${processor.getName()}: ${e}`,\n );\n }\n }\n return ret;\n };\n\n const postProcessNotification = async (\n notification: Notification,\n opts: NotificationSendOptions,\n ) => {\n const filtered = await filterProcessors(notification);\n for (const processor of filtered) {\n if (processor.postProcess) {\n try {\n await processor.postProcess(notification, opts);\n } catch (e) {\n logger.error(\n `Error while post processing notification with ${processor.getName()}: ${e}`,\n );\n }\n }\n }\n };\n\n const validateLink = (link: string) => {\n const stripLeadingSlash = (s: string) => s.replace(/^\\//, '');\n const ensureTrailingSlash = (s: string) => s.replace(/\\/?$/, '/');\n const url = new URL(\n stripLeadingSlash(link),\n ensureTrailingSlash(frontendBaseUrl),\n );\n if (url.protocol !== 'https:' && url.protocol !== 'http:') {\n throw new Error('Only HTTP/HTTPS links are allowed');\n }\n };\n\n const appendCommonOptions = (\n req: Request,\n opts: NotificationGetOptions | TopicGetOptions,\n ) => {\n if (req.query.search) {\n opts.search = req.query.search.toString();\n }\n if (req.query.read === 'true') {\n opts.read = true;\n } else if (req.query.read === 'false') {\n opts.read = false;\n // or keep undefined\n }\n\n if (req.query.saved === 'true') {\n opts.saved = true;\n } else if (req.query.saved === 'false') {\n opts.saved = false;\n // or keep undefined\n }\n if (req.query.createdAfter) {\n const sinceEpoch = Date.parse(String(req.query.createdAfter));\n if (isNaN(sinceEpoch)) {\n throw new InputError('Unexpected date format');\n }\n opts.createdAfter = new Date(sinceEpoch);\n }\n if (req.query.minimumSeverity) {\n opts.minimumSeverity = normalizeSeverity(\n req.query.minimumSeverity.toString(),\n );\n }\n };\n\n // TODO: Move to use OpenAPI router instead\n const router = Router();\n router.use(express.json());\n\n const listNotificationsHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const opts: NotificationGetOptions = {\n user: user,\n };\n if (req.query.offset) {\n opts.offset = Number.parseInt(req.query.offset.toString(), 10);\n }\n if (req.query.limit) {\n opts.limit = Number.parseInt(req.query.limit.toString(), 10);\n }\n if (req.query.orderField) {\n opts.orderField = parseEntityOrderFieldParams(req.query);\n }\n\n if (req.query.topic) {\n opts.topic = req.query.topic.toString();\n }\n\n appendCommonOptions(req, opts);\n\n const [notifications, totalCount] = await Promise.all([\n store.getNotifications(opts),\n store.getNotificationsCount(opts),\n ]);\n res.json({\n totalCount,\n notifications,\n });\n };\n\n router.get('/', listNotificationsHandler); // Deprecated endpoint\n router.get('/notifications', listNotificationsHandler);\n\n router.get('/status', async (req: Request<any, NotificationStatus>, res) => {\n const user = await getUser(req);\n const status = await store.getStatus({ user });\n res.json(status);\n });\n\n router.get(\n '/settings',\n async (req: Request<any, NotificationSettings>, res) => {\n const user = await getUser(req);\n const response = await getNotificationSettings(user);\n res.json(response);\n },\n );\n\n router.post(\n '/settings',\n async (\n req: Request<any, NotificationSettings, NotificationSettings>,\n res,\n ) => {\n const user = await getUser(req);\n const channels = getNotificationChannels();\n const settings: NotificationSettings = req.body;\n if (settings.channels.some(c => !channels.includes(c.id))) {\n throw new InputError('Invalid channel');\n }\n await store.saveNotificationSettings({ user, settings });\n const response = await getNotificationSettings(user);\n res.json(response);\n },\n );\n\n const getNotificationHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const opts: NotificationGetOptions = {\n user: user,\n limit: 1,\n ids: [req.params.id],\n };\n const notifications = await store.getNotifications(opts);\n if (notifications.length !== 1) {\n throw new NotFoundError('Not found');\n }\n res.json(notifications[0]);\n };\n\n // Get topics\n const listTopicsHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const opts: TopicGetOptions = {\n user: user,\n };\n\n appendCommonOptions(req, opts);\n\n const topics = await store.getTopics(opts);\n res.json(topics);\n };\n\n router.get('/topics', listTopicsHandler);\n\n // Make sure this is the last \"GET\" handler\n router.get('/:id', getNotificationHandler); // Deprecated endpoint\n router.get('/notifications/:id', getNotificationHandler);\n\n const updateNotificationsHandler = async (req: Request, res: Response) => {\n const user = await getUser(req);\n const { ids, read, saved } = req.body;\n if (!ids || !Array.isArray(ids)) {\n throw new InputError();\n }\n\n if (read === true) {\n await store.markRead({ user, ids });\n\n if (signals) {\n await signals.publish<NotificationReadSignal>({\n recipients: { type: 'user', entityRef: [user] },\n message: { action: 'notification_read', notification_ids: ids },\n channel: 'notifications',\n });\n }\n } else if (read === false) {\n await store.markUnread({ user: user, ids });\n\n if (signals) {\n await signals.publish<NotificationReadSignal>({\n recipients: { type: 'user', entityRef: [user] },\n message: { action: 'notification_unread', notification_ids: ids },\n channel: 'notifications',\n });\n }\n }\n\n if (saved === true) {\n await store.markSaved({ user: user, ids });\n } else if (saved === false) {\n await store.markUnsaved({ user: user, ids });\n }\n\n const notifications = await store.getNotifications({ ids, user: user });\n res.json(notifications);\n };\n\n router.post('/update', updateNotificationsHandler); // Deprecated endpoint\n router.post('/notifications/update', updateNotificationsHandler);\n\n const sendBroadcastNotification = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n opts: NotificationSendOptions,\n origin: string,\n ) => {\n const { scope } = opts.payload;\n const broadcastNotification = {\n ...baseNotification,\n user: null,\n id: uuid(),\n };\n const notification = await preProcessNotification(\n broadcastNotification,\n opts,\n );\n let existingNotification;\n if (scope) {\n existingNotification = await store.getExistingScopeBroadcast({\n scope,\n origin,\n });\n }\n\n let ret = notification;\n if (existingNotification) {\n const restored = await store.restoreExistingNotification({\n id: existingNotification.id,\n notification: { ...notification, user: '' },\n });\n ret = restored ?? notification;\n } else {\n await store.saveBroadcast(notification);\n }\n\n if (signals) {\n await signals.publish<NewNotificationSignal>({\n recipients: { type: 'broadcast' },\n message: {\n action: 'new_notification',\n notification_id: ret.id,\n },\n channel: 'notifications',\n });\n }\n postProcessNotification(ret, opts);\n return notification;\n };\n\n const sendUserNotification = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n user: string,\n opts: NotificationSendOptions,\n origin: string,\n scope?: string,\n ): Promise<Notification | undefined> => {\n const userNotification = {\n ...baseNotification,\n id: uuid(),\n user,\n };\n const notification = await preProcessNotification(userNotification, opts);\n\n const enabled = await isNotificationsEnabled({\n user,\n channel: WEB_NOTIFICATION_CHANNEL,\n origin: userNotification.origin,\n topic: userNotification.payload.topic ?? null,\n });\n\n let ret = notification;\n\n if (!enabled) {\n postProcessNotification(ret, opts);\n return undefined;\n }\n\n let existingNotification;\n if (scope) {\n existingNotification = await store.getExistingScopeNotification({\n user,\n scope,\n origin,\n });\n }\n\n if (existingNotification) {\n const restored = await store.restoreExistingNotification({\n id: existingNotification.id,\n notification,\n });\n ret = restored ?? notification;\n } else {\n await store.saveNotification(notification);\n }\n\n if (signals) {\n await signals.publish<NewNotificationSignal>({\n recipients: { type: 'user', entityRef: [user] },\n message: {\n action: 'new_notification',\n notification_id: ret.id,\n },\n channel: 'notifications',\n });\n }\n postProcessNotification(ret, opts);\n return ret;\n };\n\n const filterNonUserEntityRefs = (refs: string[]): string[] => {\n return refs.filter(ref => {\n try {\n const parsed = parseEntityRef(ref);\n return parsed.kind.toLowerCase() === 'user';\n } catch {\n return false;\n }\n });\n };\n\n const sendUserNotifications = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n users: string[],\n opts: NotificationSendOptions,\n origin: string,\n ): Promise<Notification[]> => {\n const { scope } = opts.payload;\n const uniqueUsers = [...new Set(filterNonUserEntityRefs(users))];\n const throttled = throttle((user: string) =>\n sendUserNotification(baseNotification, user, opts, origin, scope),\n );\n const sent = await Promise.all(uniqueUsers.map(user => throttled(user)));\n return sent.filter(n => n !== undefined);\n };\n\n const createNotificationHandler = async (\n req: Request<any, Notification[], NotificationSendOptions>,\n res: Response,\n ) => {\n const credentials = await httpAuth.credentials(req, {\n allow: ['service'],\n });\n\n const origin = credentials.principal.subject;\n const opts = await processOptions(req.body, origin);\n const { recipients, payload } = opts;\n const { title, link } = payload;\n const notifications: Notification[] = [];\n\n if (!recipients || !title) {\n const missing = [\n !title ? 'title' : null,\n !recipients ? 'recipients' : null,\n ].filter(Boolean);\n const err = `Invalid notification request received: missing ${missing.join(\n ', ',\n )}`;\n throw new InputError(err);\n }\n\n if (link) {\n try {\n validateLink(link);\n } catch (e) {\n throw new InputError('Invalid link provided', e);\n }\n }\n\n const baseNotification = {\n payload: {\n ...payload,\n severity: payload.severity ?? 'normal',\n },\n origin,\n created: new Date(),\n };\n\n if (recipients.type === 'broadcast') {\n const broadcast = await sendBroadcastNotification(\n baseNotification,\n opts,\n origin,\n );\n notifications.push(broadcast);\n } else if (recipients.type === 'entity') {\n const entityRefs = [recipients.entityRef].flat();\n const excludedEntityRefs = recipients.excludeEntityRef\n ? [recipients.excludeEntityRef].flat()\n : undefined;\n try {\n const { userEntityRefs } =\n await usedRecipientResolver.resolveNotificationRecipients({\n entityRefs,\n excludedEntityRefs,\n });\n const userNotifications = await sendUserNotifications(\n baseNotification,\n userEntityRefs,\n opts,\n origin,\n );\n notifications.push(...userNotifications);\n } catch (e) {\n throw new InputError('Failed to send user notifications', e);\n }\n } else {\n throw new InputError(\n `Invalid recipients type, please use either 'broadcast' or 'entity'`,\n );\n }\n\n res.json(notifications);\n };\n\n // Add new notification\n router.post('/', createNotificationHandler);\n router.post('/notifications', createNotificationHandler);\n\n return router;\n}\n"],"names":["config","durationToMilliseconds","readDurationFromConfig","pThrottle","DefaultNotificationRecipientResolver","isNotificationsEnabledFor","notificationSeverities","InputError","normalizeSeverity","Router","express","parseEntityOrderFieldParams","NotFoundError","uuid","parseEntityRef"],"mappings":"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"}
@@ -14,6 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ // @ts-check
18
+
19
+ /**
20
+ * @param {import('knex').Knex} knex
21
+ */
17
22
  exports.up = async function up(knex) {
18
23
  await knex.schema.createTable('notification', table => {
19
24
  table.uuid('id').primary();
@@ -14,6 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ // @ts-check
18
+
19
+ /**
20
+ * @param {import('knex').Knex} knex
21
+ */
17
22
  exports.up = async function up(knex) {
18
23
  await knex.schema.alterTable('notification', table => {
19
24
  table.text('link').nullable().alter();
@@ -21,6 +26,9 @@ exports.up = async function up(knex) {
21
26
  });
22
27
  };
23
28
 
29
+ /**
30
+ * @param {import('knex').Knex} knex
31
+ */
24
32
  exports.down = async function down(knex) {
25
33
  await knex.schema.alterTable('notification', table => {
26
34
  table.text('link').notNullable().alter();
@@ -14,6 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ // @ts-check
18
+
19
+ /**
20
+ * @param {import('knex').Knex} knex
21
+ */
17
22
  exports.up = async function up(knex) {
18
23
  await knex.schema.createTable('broadcast', table => {
19
24
  table.uuid('id').primary();
@@ -37,7 +42,7 @@ exports.up = async function up(knex) {
37
42
 
38
43
  table
39
44
  .foreign('broadcast_id')
40
- .references(['id'])
45
+ .references('id')
41
46
  .inTable('broadcast')
42
47
  .onDelete('CASCADE');
43
48
  table.unique(['broadcast_id', 'user'], {
@@ -13,6 +13,12 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+
17
+ // @ts-check
18
+
19
+ /**
20
+ * @param {import('knex').Knex} knex
21
+ */
16
22
  exports.up = async function up(knex) {
17
23
  await knex.schema.createTable('user_settings', table => {
18
24
  table.string('user').notNullable();
@@ -14,6 +14,11 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
+ // @ts-check
18
+
19
+ /**
20
+ * @param {import('knex').Knex} knex
21
+ */
17
22
  exports.up = async function up(knex) {
18
23
  await knex.schema.alterTable('notification', table => {
19
24
  table.string('icon', 255).nullable();
@@ -23,6 +28,9 @@ exports.up = async function up(knex) {
23
28
  });
24
29
  };
25
30
 
31
+ /**
32
+ * @param {import('knex').Knex} knex
33
+ */
26
34
  exports.down = async function down(knex) {
27
35
  await knex.schema.alterTable('notification', table => {
28
36
  table.dropColumn('icon');
@@ -13,8 +13,14 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+
17
+ // @ts-check
18
+
16
19
  const crypto = require('crypto');
17
20
 
21
+ /**
22
+ * @param {import('knex').Knex} knex
23
+ */
18
24
  exports.up = async function up(knex) {
19
25
  await knex.schema.alterTable('user_settings', table => {
20
26
  table.string('topic').nullable().after('origin');
@@ -41,6 +47,9 @@ exports.up = async function up(knex) {
41
47
  });
42
48
  };
43
49
 
50
+ /**
51
+ * @param {import('knex').Knex} knex
52
+ */
44
53
  exports.down = async function down(knex) {
45
54
  await knex.schema.table('user_settings', table => {
46
55
  table.dropUnique([], 'user_settings_unique_idx');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-notifications-backend",
3
- "version": "0.5.10-next.0",
3
+ "version": "0.5.10",
4
4
  "backstage": {
5
5
  "role": "backend-plugin",
6
6
  "pluginId": "notifications",
@@ -42,17 +42,17 @@
42
42
  "test": "backstage-cli package test"
43
43
  },
44
44
  "dependencies": {
45
- "@backstage/backend-plugin-api": "1.4.3-next.0",
46
- "@backstage/catalog-model": "1.7.5",
47
- "@backstage/config": "1.3.3",
48
- "@backstage/errors": "1.2.7",
49
- "@backstage/plugin-auth-node": "0.6.7-next.0",
50
- "@backstage/plugin-catalog-node": "1.18.1-next.0",
51
- "@backstage/plugin-events-node": "0.4.15-next.0",
52
- "@backstage/plugin-notifications-common": "0.1.0",
53
- "@backstage/plugin-notifications-node": "0.2.19-next.0",
54
- "@backstage/plugin-signals-node": "0.1.24-next.0",
55
- "@backstage/types": "1.2.1",
45
+ "@backstage/backend-plugin-api": "^1.4.3",
46
+ "@backstage/catalog-model": "^1.7.5",
47
+ "@backstage/config": "^1.3.3",
48
+ "@backstage/errors": "^1.2.7",
49
+ "@backstage/plugin-auth-node": "^0.6.7",
50
+ "@backstage/plugin-catalog-node": "^1.19.0",
51
+ "@backstage/plugin-events-node": "^0.4.15",
52
+ "@backstage/plugin-notifications-common": "^0.1.0",
53
+ "@backstage/plugin-notifications-node": "^0.2.19",
54
+ "@backstage/plugin-signals-node": "^0.1.24",
55
+ "@backstage/types": "^1.2.2",
56
56
  "express": "^4.17.1",
57
57
  "express-promise-router": "^4.1.0",
58
58
  "knex": "^3.0.0",
@@ -62,13 +62,13 @@
62
62
  "yn": "^4.0.0"
63
63
  },
64
64
  "devDependencies": {
65
- "@backstage/backend-defaults": "0.12.1-next.0",
66
- "@backstage/backend-test-utils": "1.9.0-next.1",
67
- "@backstage/cli": "0.34.2-next.1",
68
- "@backstage/plugin-auth-backend": "0.25.4-next.0",
69
- "@backstage/plugin-auth-backend-module-guest-provider": "0.2.12-next.0",
70
- "@backstage/plugin-events-backend": "0.5.6-next.0",
71
- "@backstage/plugin-signals-backend": "0.3.8-next.0",
65
+ "@backstage/backend-defaults": "^0.12.1",
66
+ "@backstage/backend-test-utils": "^1.9.0",
67
+ "@backstage/cli": "^0.34.2",
68
+ "@backstage/plugin-auth-backend": "^0.25.4",
69
+ "@backstage/plugin-auth-backend-module-guest-provider": "^0.2.12",
70
+ "@backstage/plugin-events-backend": "^0.5.6",
71
+ "@backstage/plugin-signals-backend": "^0.3.8",
72
72
  "@types/express": "^4.17.6",
73
73
  "@types/supertest": "^2.0.8",
74
74
  "msw": "^1.0.0",
@@ -1,96 +0,0 @@
1
- 'use strict';
2
-
3
- var catalogModel = require('@backstage/catalog-model');
4
-
5
- const isUserEntityRef = (ref) => catalogModel.parseEntityRef(ref).kind.toLocaleLowerCase() === "user";
6
- const partitionEntityRefs = (refs) => refs.reduce(
7
- ([userEntityRefs, otherEntityRefs], ref) => {
8
- return isUserEntityRef(ref) ? [[...userEntityRefs, ref], otherEntityRefs] : [userEntityRefs, [...otherEntityRefs, ref]];
9
- },
10
- [[], []]
11
- );
12
- const getUsersForEntityRef = async (entityRef, excludeEntityRefs, options) => {
13
- const { auth, catalog } = options;
14
- if (entityRef === null) {
15
- return [];
16
- }
17
- const excluded = Array.isArray(excludeEntityRefs) ? excludeEntityRefs : [excludeEntityRefs];
18
- const refsArr = Array.isArray(entityRef) ? entityRef : [entityRef];
19
- const [userEntityRefs, otherEntityRefs] = partitionEntityRefs(refsArr);
20
- const users = userEntityRefs.filter((ref) => !excluded.includes(ref));
21
- const entityRefs = otherEntityRefs.filter((ref) => !excluded.includes(ref));
22
- const fields = ["kind", "metadata.name", "metadata.namespace", "relations"];
23
- let entities = [];
24
- if (entityRefs.length > 0) {
25
- const fetchedEntities = await catalog.getEntitiesByRefs(
26
- {
27
- entityRefs,
28
- fields
29
- },
30
- { credentials: await auth.getOwnServiceCredentials() }
31
- );
32
- entities = fetchedEntities.items;
33
- }
34
- const mapEntity = async (entity) => {
35
- if (!entity) {
36
- return [];
37
- }
38
- const currentEntityRef = catalogModel.stringifyEntityRef(entity);
39
- if (excluded.includes(currentEntityRef)) {
40
- return [];
41
- }
42
- if (catalogModel.isUserEntity(entity)) {
43
- return [currentEntityRef];
44
- }
45
- if (catalogModel.isGroupEntity(entity)) {
46
- if (!entity.relations?.length) {
47
- return [];
48
- }
49
- const groupUsers = entity.relations.filter(
50
- (relation) => relation.type === catalogModel.RELATION_HAS_MEMBER && isUserEntityRef(relation.targetRef)
51
- ).map((r) => r.targetRef);
52
- const childGroupRefs = entity.relations.filter((relation) => relation.type === catalogModel.RELATION_PARENT_OF).map((r) => r.targetRef);
53
- let childGroupUsers = [];
54
- if (childGroupRefs.length > 0) {
55
- const childGroups = await catalog.getEntitiesByRefs(
56
- {
57
- entityRefs: childGroupRefs,
58
- fields
59
- },
60
- { credentials: await auth.getOwnServiceCredentials() }
61
- );
62
- childGroupUsers = await Promise.all(childGroups.items.map(mapEntity));
63
- }
64
- return [...groupUsers, ...childGroupUsers.flat(2)].filter(
65
- (ref) => !excluded.includes(ref)
66
- );
67
- }
68
- if (entity.relations?.length) {
69
- const ownerRef = entity.relations.find(
70
- (relation) => relation.type === catalogModel.RELATION_OWNED_BY
71
- )?.targetRef;
72
- if (!ownerRef) {
73
- return [];
74
- }
75
- if (isUserEntityRef(ownerRef)) {
76
- if (excluded.includes(ownerRef)) {
77
- return [];
78
- }
79
- return [ownerRef];
80
- }
81
- const owner = await catalog.getEntityByRef(ownerRef, {
82
- credentials: await auth.getOwnServiceCredentials()
83
- });
84
- return mapEntity(owner);
85
- }
86
- return [];
87
- };
88
- for (const entity of entities) {
89
- const u = await mapEntity(entity);
90
- users.push(...u);
91
- }
92
- return [...new Set(users)].filter(Boolean);
93
- };
94
-
95
- exports.getUsersForEntityRef = getUsersForEntityRef;
96
- //# sourceMappingURL=getUsersForEntityRef.cjs.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getUsersForEntityRef.cjs.js","sources":["../../src/service/getUsersForEntityRef.ts"],"sourcesContent":["/*\n * Copyright 2024 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 Entity,\n isGroupEntity,\n isUserEntity,\n parseEntityRef,\n RELATION_HAS_MEMBER,\n RELATION_OWNED_BY,\n RELATION_PARENT_OF,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { AuthService } from '@backstage/backend-plugin-api';\nimport { CatalogService } from '@backstage/plugin-catalog-node';\n\nconst isUserEntityRef = (ref: string) =>\n parseEntityRef(ref).kind.toLocaleLowerCase() === 'user';\n\n// Partitions array of entity references to two arrays; user entity refs and other entity refs\nconst partitionEntityRefs = (refs: string[]): string[][] =>\n refs.reduce(\n ([userEntityRefs, otherEntityRefs]: string[][], ref: string) => {\n return isUserEntityRef(ref)\n ? [[...userEntityRefs, ref], otherEntityRefs]\n : [userEntityRefs, [...otherEntityRefs, ref]];\n },\n [[], []],\n );\n\nexport const getUsersForEntityRef = async (\n entityRef: string | string[] | null,\n excludeEntityRefs: string | string[],\n options: {\n auth: AuthService;\n catalog: CatalogService;\n },\n): Promise<string[]> => {\n const { auth, catalog } = options;\n\n if (entityRef === null) {\n return [];\n }\n\n const excluded = Array.isArray(excludeEntityRefs)\n ? excludeEntityRefs\n : [excludeEntityRefs];\n\n const refsArr = Array.isArray(entityRef) ? entityRef : [entityRef];\n const [userEntityRefs, otherEntityRefs] = partitionEntityRefs(refsArr);\n const users: string[] = userEntityRefs.filter(ref => !excluded.includes(ref));\n const entityRefs = otherEntityRefs.filter(ref => !excluded.includes(ref));\n\n const fields = ['kind', 'metadata.name', 'metadata.namespace', 'relations'];\n let entities: Array<Entity | undefined> = [];\n if (entityRefs.length > 0) {\n const fetchedEntities = await catalog.getEntitiesByRefs(\n {\n entityRefs,\n fields,\n },\n { credentials: await auth.getOwnServiceCredentials() },\n );\n entities = fetchedEntities.items;\n }\n\n const mapEntity = async (entity: Entity | undefined): Promise<string[]> => {\n if (!entity) {\n return [];\n }\n\n const currentEntityRef = stringifyEntityRef(entity);\n if (excluded.includes(currentEntityRef)) {\n return [];\n }\n\n if (isUserEntity(entity)) {\n return [currentEntityRef];\n }\n\n if (isGroupEntity(entity)) {\n if (!entity.relations?.length) {\n return [];\n }\n\n const groupUsers = entity.relations\n .filter(\n relation =>\n relation.type === RELATION_HAS_MEMBER &&\n isUserEntityRef(relation.targetRef),\n )\n .map(r => r.targetRef);\n\n const childGroupRefs = entity.relations\n .filter(relation => relation.type === RELATION_PARENT_OF)\n .map(r => r.targetRef);\n\n let childGroupUsers: string[][] = [];\n if (childGroupRefs.length > 0) {\n const childGroups = await catalog.getEntitiesByRefs(\n {\n entityRefs: childGroupRefs,\n fields,\n },\n { credentials: await auth.getOwnServiceCredentials() },\n );\n childGroupUsers = await Promise.all(childGroups.items.map(mapEntity));\n }\n\n return [...groupUsers, ...childGroupUsers.flat(2)].filter(\n ref => !excluded.includes(ref),\n );\n }\n\n if (entity.relations?.length) {\n const ownerRef = entity.relations.find(\n relation => relation.type === RELATION_OWNED_BY,\n )?.targetRef;\n\n if (!ownerRef) {\n return [];\n }\n\n if (isUserEntityRef(ownerRef)) {\n if (excluded.includes(ownerRef)) {\n return [];\n }\n return [ownerRef];\n }\n\n const owner = await catalog.getEntityByRef(ownerRef, {\n credentials: await auth.getOwnServiceCredentials(),\n });\n return mapEntity(owner);\n }\n\n return [];\n };\n\n for (const entity of entities) {\n const u = await mapEntity(entity);\n users.push(...u);\n }\n\n return [...new Set(users)].filter(Boolean);\n};\n"],"names":["parseEntityRef","stringifyEntityRef","isUserEntity","isGroupEntity","RELATION_HAS_MEMBER","RELATION_PARENT_OF","RELATION_OWNED_BY"],"mappings":"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"}