@backstage/plugin-notifications-backend 0.1.0-next.2 → 0.1.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 +57 -0
- package/dist/index.cjs.js +289 -108
- package/dist/index.cjs.js.map +1 -1
- package/migrations/20231215_init.js +44 -0
- package/migrations/20240221_removeDone.js +29 -0
- package/migrations/20240313_broadcast.js +55 -0
- package/package.json +16 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,62 @@
|
|
|
1
1
|
# @backstage/plugin-notifications-backend
|
|
2
2
|
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies
|
|
8
|
+
- @backstage/catalog-client@1.6.2
|
|
9
|
+
- @backstage/backend-common@0.21.5
|
|
10
|
+
- @backstage/plugin-auth-node@0.4.10
|
|
11
|
+
- @backstage/plugin-notifications-node@0.1.1
|
|
12
|
+
- @backstage/plugin-events-node@0.3.1
|
|
13
|
+
- @backstage/plugin-signals-node@0.1.1
|
|
14
|
+
- @backstage/backend-plugin-api@0.6.15
|
|
15
|
+
- @backstage/catalog-model@1.4.5
|
|
16
|
+
- @backstage/config@1.2.0
|
|
17
|
+
- @backstage/errors@1.2.4
|
|
18
|
+
- @backstage/plugin-notifications-common@0.0.2
|
|
19
|
+
|
|
20
|
+
## 0.1.0
|
|
21
|
+
|
|
22
|
+
### Minor Changes
|
|
23
|
+
|
|
24
|
+
- 6e6d096: notifications can be newly sorted by list of predefined options
|
|
25
|
+
- cd96173: Notifications-backend URL query parameters changed from `sort/sortOrder` to `orderField` and `created_after` to `createdAfter` to unify with other plugins.
|
|
26
|
+
- 07abfe1: The NotificationsPage newly uses pagination implemented on the backend layer to avoid large dataset transfers
|
|
27
|
+
- daf85dc: BREAKING CHANGE: Migrates signals to use the `EventsService` and makes it mandatory
|
|
28
|
+
- 758f2a4: The Notifications frontend has been redesigned towards list view with condensed row details. The 'done' attribute has been removed to keep the Notifications aligned with the idea of a messaging system instead of a task manager.
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- ba14c0e: Support for broadcast notifications
|
|
33
|
+
- dff7a7e: All notifications can be marked and filtered by severity critical, high, normal or low, the default is 'normal'
|
|
34
|
+
- 4467036: Allow unauthenticated access to health check endpoint.
|
|
35
|
+
- 6c1547a: **BREAKING** Type definition added to signal recipients
|
|
36
|
+
|
|
37
|
+
Update to use `{type: 'broadcast'}` instead `null` and `{type: 'user', entityRef: ''}`
|
|
38
|
+
instead string entity references
|
|
39
|
+
|
|
40
|
+
- 75f2d84: the user can newly mark notifications as "Saved" for their better visibility in the future
|
|
41
|
+
- a790a3d: Move notification origin resolving to backend with new auth
|
|
42
|
+
- 5d9c5ba: The Notifications can be newly filtered based on the Created Date.
|
|
43
|
+
- 0fb419b: Updated dependency `uuid` to `^9.0.0`.
|
|
44
|
+
Updated dependency `@types/uuid` to `^9.0.0`.
|
|
45
|
+
- 84af361: Migrated to using the new auth services.
|
|
46
|
+
- 6d84ee6: Changed to use the refactored signal service naming
|
|
47
|
+
- Updated dependencies
|
|
48
|
+
- @backstage/plugin-notifications-common@0.0.2
|
|
49
|
+
- @backstage/plugin-notifications-node@0.1.0
|
|
50
|
+
- @backstage/plugin-events-node@0.3.0
|
|
51
|
+
- @backstage/backend-common@0.21.4
|
|
52
|
+
- @backstage/plugin-auth-node@0.4.9
|
|
53
|
+
- @backstage/config@1.2.0
|
|
54
|
+
- @backstage/errors@1.2.4
|
|
55
|
+
- @backstage/plugin-signals-node@0.1.0
|
|
56
|
+
- @backstage/backend-plugin-api@0.6.14
|
|
57
|
+
- @backstage/catalog-client@1.6.1
|
|
58
|
+
- @backstage/catalog-model@1.4.5
|
|
59
|
+
|
|
3
60
|
## 0.1.0-next.2
|
|
4
61
|
|
|
5
62
|
### Minor Changes
|
package/dist/index.cjs.js
CHANGED
|
@@ -13,10 +13,10 @@ var errors = require('@backstage/errors');
|
|
|
13
13
|
var pluginSignalsNode = require('@backstage/plugin-signals-node');
|
|
14
14
|
var pluginNotificationsNode = require('@backstage/plugin-notifications-node');
|
|
15
15
|
|
|
16
|
-
function
|
|
16
|
+
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
17
17
|
|
|
18
|
-
var express__default = /*#__PURE__*/
|
|
19
|
-
var Router__default = /*#__PURE__*/
|
|
18
|
+
var express__default = /*#__PURE__*/_interopDefaultCompat(express);
|
|
19
|
+
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
20
20
|
|
|
21
21
|
var __defProp = Object.defineProperty;
|
|
22
22
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -28,9 +28,38 @@ const migrationsDir = backendCommon.resolvePackagePath(
|
|
|
28
28
|
"@backstage/plugin-notifications-backend",
|
|
29
29
|
"migrations"
|
|
30
30
|
);
|
|
31
|
+
const NOTIFICATION_COLUMNS = [
|
|
32
|
+
"id",
|
|
33
|
+
"title",
|
|
34
|
+
"description",
|
|
35
|
+
"severity",
|
|
36
|
+
"link",
|
|
37
|
+
"origin",
|
|
38
|
+
"scope",
|
|
39
|
+
"topic",
|
|
40
|
+
"created",
|
|
41
|
+
"updated",
|
|
42
|
+
"user",
|
|
43
|
+
"read",
|
|
44
|
+
"saved"
|
|
45
|
+
];
|
|
46
|
+
const severities = [
|
|
47
|
+
"critical",
|
|
48
|
+
"high",
|
|
49
|
+
"normal",
|
|
50
|
+
"low"
|
|
51
|
+
];
|
|
52
|
+
const normalizeSeverity = (input) => {
|
|
53
|
+
let lower = (input != null ? input : "normal").toLowerCase();
|
|
54
|
+
if (severities.indexOf(lower) < 0) {
|
|
55
|
+
lower = "normal";
|
|
56
|
+
}
|
|
57
|
+
return lower;
|
|
58
|
+
};
|
|
31
59
|
class DatabaseNotificationsStore {
|
|
32
60
|
constructor(db) {
|
|
33
61
|
this.db = db;
|
|
62
|
+
__publicField(this, "isSQLite", false);
|
|
34
63
|
__publicField(this, "mapToInteger", (val) => {
|
|
35
64
|
return typeof val === "string" ? Number.parseInt(val, 10) : val != null ? val : 0;
|
|
36
65
|
});
|
|
@@ -65,35 +94,52 @@ class DatabaseNotificationsStore {
|
|
|
65
94
|
link: (_b = notification.payload) == null ? void 0 : _b.link,
|
|
66
95
|
title: (_c = notification.payload) == null ? void 0 : _c.title,
|
|
67
96
|
description: (_d = notification.payload) == null ? void 0 : _d.description,
|
|
68
|
-
severity: (_e = notification.payload) == null ? void 0 : _e.severity,
|
|
97
|
+
severity: normalizeSeverity((_e = notification.payload) == null ? void 0 : _e.severity),
|
|
69
98
|
scope: (_f = notification.payload) == null ? void 0 : _f.scope,
|
|
70
99
|
saved: notification.saved,
|
|
71
100
|
read: notification.read
|
|
72
101
|
};
|
|
73
102
|
});
|
|
103
|
+
__publicField(this, "mapBroadcastToDbRow", (notification) => {
|
|
104
|
+
var _a, _b, _c, _d, _e, _f;
|
|
105
|
+
return {
|
|
106
|
+
id: notification.id,
|
|
107
|
+
origin: notification.origin,
|
|
108
|
+
created: notification.created,
|
|
109
|
+
topic: (_a = notification.payload) == null ? void 0 : _a.topic,
|
|
110
|
+
link: (_b = notification.payload) == null ? void 0 : _b.link,
|
|
111
|
+
title: (_c = notification.payload) == null ? void 0 : _c.title,
|
|
112
|
+
description: (_d = notification.payload) == null ? void 0 : _d.description,
|
|
113
|
+
severity: normalizeSeverity((_e = notification.payload) == null ? void 0 : _e.severity),
|
|
114
|
+
scope: (_f = notification.payload) == null ? void 0 : _f.scope
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
__publicField(this, "getBroadcastUnion", () => {
|
|
118
|
+
return this.db("broadcast").leftJoin(
|
|
119
|
+
"broadcast_user_status",
|
|
120
|
+
"id",
|
|
121
|
+
"=",
|
|
122
|
+
"broadcast_user_status.broadcast_id"
|
|
123
|
+
).select(NOTIFICATION_COLUMNS);
|
|
124
|
+
});
|
|
74
125
|
__publicField(this, "getNotificationsBaseQuery", (options) => {
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
126
|
+
const { user, orderField } = options;
|
|
127
|
+
const subQuery = this.db("notification").select(NOTIFICATION_COLUMNS).unionAll([this.getBroadcastUnion()]).as("notifications");
|
|
128
|
+
const query = this.db.from(subQuery).where((q) => {
|
|
129
|
+
q.where("user", user).orWhereNull("user");
|
|
130
|
+
});
|
|
131
|
+
if (orderField && orderField.length > 0) {
|
|
132
|
+
orderField.forEach((orderBy) => {
|
|
133
|
+
query.orderBy(orderBy.field, orderBy.order);
|
|
134
|
+
});
|
|
135
|
+
} else if (!orderField) {
|
|
136
|
+
query.orderBy("created", "desc");
|
|
83
137
|
}
|
|
84
138
|
if (options.createdAfter) {
|
|
85
|
-
if (isSQLite) {
|
|
86
|
-
query.where(
|
|
87
|
-
"notification.created",
|
|
88
|
-
">=",
|
|
89
|
-
options.createdAfter.valueOf()
|
|
90
|
-
);
|
|
139
|
+
if (this.isSQLite) {
|
|
140
|
+
query.where("created", ">=", options.createdAfter.valueOf());
|
|
91
141
|
} else {
|
|
92
|
-
query.where(
|
|
93
|
-
"notification.created",
|
|
94
|
-
">=",
|
|
95
|
-
options.createdAfter.toISOString()
|
|
96
|
-
);
|
|
142
|
+
query.where("created", ">=", options.createdAfter.toISOString());
|
|
97
143
|
}
|
|
98
144
|
}
|
|
99
145
|
if (options.limit) {
|
|
@@ -104,25 +150,58 @@ class DatabaseNotificationsStore {
|
|
|
104
150
|
}
|
|
105
151
|
if (options.search) {
|
|
106
152
|
query.whereRaw(
|
|
107
|
-
`(LOWER(
|
|
153
|
+
`(LOWER(title) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?))`,
|
|
108
154
|
[`%${options.search}%`, `%${options.search}%`]
|
|
109
155
|
);
|
|
110
156
|
}
|
|
111
157
|
if (options.ids) {
|
|
112
|
-
query.whereIn("
|
|
158
|
+
query.whereIn("id", options.ids);
|
|
113
159
|
}
|
|
114
160
|
if (options.read) {
|
|
115
|
-
query.whereNotNull("
|
|
161
|
+
query.whereNotNull("read");
|
|
116
162
|
} else if (options.read === false) {
|
|
117
|
-
query.whereNull("
|
|
163
|
+
query.whereNull("read");
|
|
118
164
|
}
|
|
119
165
|
if (options.saved) {
|
|
120
|
-
query.whereNotNull("
|
|
166
|
+
query.whereNotNull("saved");
|
|
121
167
|
} else if (options.saved === false) {
|
|
122
|
-
query.whereNull("
|
|
168
|
+
query.whereNull("saved");
|
|
169
|
+
}
|
|
170
|
+
if (options.minimumSeverity !== void 0) {
|
|
171
|
+
const idx = severities.indexOf(options.minimumSeverity);
|
|
172
|
+
const equalOrHigher = severities.slice(0, idx + 1);
|
|
173
|
+
query.whereIn("severity", equalOrHigher);
|
|
123
174
|
}
|
|
124
175
|
return query;
|
|
125
176
|
});
|
|
177
|
+
__publicField(this, "markReadSaved", async (ids, user, read, saved) => {
|
|
178
|
+
await this.db("notification").whereIn("id", ids).where("user", user).update({ read, saved });
|
|
179
|
+
const broadcasts = this.mapToNotifications(
|
|
180
|
+
await this.db("broadcast").whereIn("id", ids).select()
|
|
181
|
+
);
|
|
182
|
+
if (broadcasts.length > 0)
|
|
183
|
+
if (!this.isSQLite) {
|
|
184
|
+
await this.db("broadcast_user_status").insert(
|
|
185
|
+
broadcasts.map((b) => ({
|
|
186
|
+
broadcast_id: b.id,
|
|
187
|
+
user,
|
|
188
|
+
read,
|
|
189
|
+
saved
|
|
190
|
+
}))
|
|
191
|
+
).onConflict(["broadcast_id", "user"]).merge(["read", "saved"]);
|
|
192
|
+
} else {
|
|
193
|
+
for (const b of broadcasts) {
|
|
194
|
+
const baseQuery = this.db("broadcast_user_status").where("broadcast_id", b.id).where("user", user);
|
|
195
|
+
const exists = await baseQuery.clone().limit(1).select().first();
|
|
196
|
+
if (exists) {
|
|
197
|
+
await baseQuery.clone().update({ read, saved });
|
|
198
|
+
} else {
|
|
199
|
+
await baseQuery.clone().insert({ broadcast_id: b.id, user, read, saved });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
this.isSQLite = this.db.client.config.client.includes("sqlite3");
|
|
126
205
|
}
|
|
127
206
|
static async create({
|
|
128
207
|
database,
|
|
@@ -139,25 +218,36 @@ class DatabaseNotificationsStore {
|
|
|
139
218
|
}
|
|
140
219
|
async getNotifications(options) {
|
|
141
220
|
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
142
|
-
const notifications = await notificationQuery.select();
|
|
221
|
+
const notifications = await notificationQuery.select(NOTIFICATION_COLUMNS);
|
|
143
222
|
return this.mapToNotifications(notifications);
|
|
144
223
|
}
|
|
145
224
|
async getNotificationsCount(options) {
|
|
146
225
|
const countOptions = { ...options };
|
|
147
226
|
countOptions.limit = void 0;
|
|
148
227
|
countOptions.offset = void 0;
|
|
149
|
-
countOptions.
|
|
228
|
+
countOptions.orderField = [];
|
|
150
229
|
const notificationQuery = this.getNotificationsBaseQuery(countOptions);
|
|
151
|
-
const response = await notificationQuery.count("
|
|
230
|
+
const response = await notificationQuery.count("id as CNT");
|
|
152
231
|
return Number(response[0].CNT);
|
|
153
232
|
}
|
|
154
233
|
async saveNotification(notification) {
|
|
155
234
|
await this.db.insert(this.mapNotificationToDbRow(notification)).into("notification");
|
|
156
235
|
}
|
|
236
|
+
async saveBroadcast(notification) {
|
|
237
|
+
await this.db.insert(this.mapBroadcastToDbRow(notification)).into("broadcast");
|
|
238
|
+
if (notification.saved || notification.read) {
|
|
239
|
+
await this.db.insert({
|
|
240
|
+
user: notification.user,
|
|
241
|
+
broadcast_id: notification.id,
|
|
242
|
+
saved: notification.saved,
|
|
243
|
+
read: notification.read
|
|
244
|
+
}).into("broadcast_user_status");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
157
247
|
async getStatus(options) {
|
|
158
248
|
const notificationQuery = this.getNotificationsBaseQuery({
|
|
159
249
|
...options,
|
|
160
|
-
|
|
250
|
+
orderField: []
|
|
161
251
|
});
|
|
162
252
|
const readSubQuery = notificationQuery.clone().count("id").whereNotNull("read").as("READ");
|
|
163
253
|
const unreadSubQuery = notificationQuery.clone().count("id").whereNull("read").as("UNREAD");
|
|
@@ -168,69 +258,93 @@ class DatabaseNotificationsStore {
|
|
|
168
258
|
};
|
|
169
259
|
}
|
|
170
260
|
async getExistingScopeNotification(options) {
|
|
171
|
-
const query = this.db("notification").where("user", options.user).where("scope", options.scope).where("origin", options.origin).
|
|
261
|
+
const query = this.db("notification").where("user", options.user).where("scope", options.scope).where("origin", options.origin).limit(1);
|
|
172
262
|
const rows = await query;
|
|
173
263
|
if (!rows || rows.length === 0) {
|
|
174
264
|
return null;
|
|
175
265
|
}
|
|
176
266
|
return rows[0];
|
|
177
267
|
}
|
|
178
|
-
async
|
|
179
|
-
const query = this.db("
|
|
180
|
-
await query
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
268
|
+
async getExistingScopeBroadcast(options) {
|
|
269
|
+
const query = this.db("broadcast").where("scope", options.scope).where("origin", options.origin).limit(1);
|
|
270
|
+
const rows = await query;
|
|
271
|
+
if (!rows || rows.length === 0) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
return rows[0];
|
|
275
|
+
}
|
|
276
|
+
async restoreExistingNotification({
|
|
277
|
+
id,
|
|
278
|
+
notification
|
|
279
|
+
}) {
|
|
280
|
+
var _a;
|
|
281
|
+
const updateColumns = {
|
|
282
|
+
title: notification.payload.title,
|
|
283
|
+
description: notification.payload.description,
|
|
284
|
+
link: notification.payload.link,
|
|
285
|
+
topic: notification.payload.topic,
|
|
185
286
|
updated: /* @__PURE__ */ new Date(),
|
|
186
|
-
severity:
|
|
287
|
+
severity: normalizeSeverity((_a = notification.payload) == null ? void 0 : _a.severity),
|
|
187
288
|
read: null
|
|
188
|
-
}
|
|
189
|
-
|
|
289
|
+
};
|
|
290
|
+
const notificationQuery = this.db("notification").where("id", id).where("user", notification.user);
|
|
291
|
+
const broadcastQuery = this.db("broadcast").where("id", id);
|
|
292
|
+
await Promise.all([
|
|
293
|
+
notificationQuery.update(updateColumns),
|
|
294
|
+
broadcastQuery.update({ ...updateColumns, read: void 0 })
|
|
295
|
+
]);
|
|
296
|
+
return await this.getNotification({ id });
|
|
190
297
|
}
|
|
191
298
|
async getNotification(options) {
|
|
192
|
-
const rows = await this.db
|
|
299
|
+
const rows = await this.db.select("*").from(
|
|
300
|
+
this.db("notification").select(NOTIFICATION_COLUMNS).unionAll([this.getBroadcastUnion()]).as("notifications")
|
|
301
|
+
).where("id", options.id).limit(1);
|
|
193
302
|
if (!rows || rows.length === 0) {
|
|
194
303
|
return null;
|
|
195
304
|
}
|
|
196
305
|
return this.mapToNotifications(rows)[0];
|
|
197
306
|
}
|
|
198
307
|
async markRead(options) {
|
|
199
|
-
|
|
200
|
-
await notificationQuery.update({ read: /* @__PURE__ */ new Date() });
|
|
308
|
+
await this.markReadSaved(options.ids, options.user, /* @__PURE__ */ new Date(), void 0);
|
|
201
309
|
}
|
|
202
310
|
async markUnread(options) {
|
|
203
|
-
|
|
204
|
-
await notificationQuery.update({ read: null });
|
|
311
|
+
await this.markReadSaved(options.ids, options.user, null, void 0);
|
|
205
312
|
}
|
|
206
313
|
async markSaved(options) {
|
|
207
|
-
|
|
208
|
-
await notificationQuery.update({ saved: /* @__PURE__ */ new Date() });
|
|
314
|
+
await this.markReadSaved(options.ids, options.user, void 0, /* @__PURE__ */ new Date());
|
|
209
315
|
}
|
|
210
316
|
async markUnsaved(options) {
|
|
211
|
-
|
|
212
|
-
await notificationQuery.update({ saved: null });
|
|
317
|
+
await this.markReadSaved(options.ids, options.user, void 0, null);
|
|
213
318
|
}
|
|
214
319
|
}
|
|
215
320
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
"topic",
|
|
220
|
-
"origin"
|
|
221
|
-
];
|
|
222
|
-
if (valid.includes(input)) {
|
|
223
|
-
return input;
|
|
321
|
+
function parseStringsParam(param, ctx) {
|
|
322
|
+
if (param === void 0) {
|
|
323
|
+
return void 0;
|
|
224
324
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const valid = ["asc", "desc"];
|
|
229
|
-
if (valid.includes(input)) {
|
|
230
|
-
return input;
|
|
325
|
+
const array = [param].flat();
|
|
326
|
+
if (array.some((p) => typeof p !== "string")) {
|
|
327
|
+
throw new errors.InputError(`Invalid ${ctx}, not a string`);
|
|
231
328
|
}
|
|
232
|
-
return
|
|
233
|
-
}
|
|
329
|
+
return array;
|
|
330
|
+
}
|
|
331
|
+
function parseEntityOrderFieldParams(params) {
|
|
332
|
+
const orderFieldStrings = parseStringsParam(params.orderField, "orderField");
|
|
333
|
+
if (!orderFieldStrings) {
|
|
334
|
+
return void 0;
|
|
335
|
+
}
|
|
336
|
+
return orderFieldStrings.map((orderFieldString) => {
|
|
337
|
+
const [field, order] = orderFieldString.split(",");
|
|
338
|
+
if (order !== void 0 && !isOrder(order)) {
|
|
339
|
+
throw new errors.InputError("Invalid order field order, must be asc or desc");
|
|
340
|
+
}
|
|
341
|
+
return { field, order };
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function isOrder(order) {
|
|
345
|
+
return ["asc", "desc"].includes(order);
|
|
346
|
+
}
|
|
347
|
+
|
|
234
348
|
async function createRouter(options) {
|
|
235
349
|
const {
|
|
236
350
|
logger,
|
|
@@ -320,8 +434,8 @@ async function createRouter(options) {
|
|
|
320
434
|
}
|
|
321
435
|
}
|
|
322
436
|
};
|
|
323
|
-
const router = Router__default
|
|
324
|
-
router.use(express__default
|
|
437
|
+
const router = Router__default.default();
|
|
438
|
+
router.use(express__default.default.json());
|
|
325
439
|
router.get("/health", (_, response) => {
|
|
326
440
|
logger.info("PONG!");
|
|
327
441
|
response.json({ status: "ok" });
|
|
@@ -337,11 +451,8 @@ async function createRouter(options) {
|
|
|
337
451
|
if (req.query.limit) {
|
|
338
452
|
opts.limit = Number.parseInt(req.query.limit.toString(), 10);
|
|
339
453
|
}
|
|
340
|
-
if (req.query.
|
|
341
|
-
opts.
|
|
342
|
-
}
|
|
343
|
-
if (req.query.sort_order) {
|
|
344
|
-
opts.sortOrder = getSortOrder(req.query.sort_order.toString());
|
|
454
|
+
if (req.query.orderField) {
|
|
455
|
+
opts.orderField = parseEntityOrderFieldParams(req.query);
|
|
345
456
|
}
|
|
346
457
|
if (req.query.search) {
|
|
347
458
|
opts.search = req.query.search.toString();
|
|
@@ -351,13 +462,23 @@ async function createRouter(options) {
|
|
|
351
462
|
} else if (req.query.read === "false") {
|
|
352
463
|
opts.read = false;
|
|
353
464
|
}
|
|
354
|
-
if (req.query.
|
|
355
|
-
|
|
465
|
+
if (req.query.saved === "true") {
|
|
466
|
+
opts.saved = true;
|
|
467
|
+
} else if (req.query.saved === "false") {
|
|
468
|
+
opts.saved = false;
|
|
469
|
+
}
|
|
470
|
+
if (req.query.createdAfter) {
|
|
471
|
+
const sinceEpoch = Date.parse(String(req.query.createdAfter));
|
|
356
472
|
if (isNaN(sinceEpoch)) {
|
|
357
473
|
throw new errors.InputError("Unexpected date format");
|
|
358
474
|
}
|
|
359
475
|
opts.createdAfter = new Date(sinceEpoch);
|
|
360
476
|
}
|
|
477
|
+
if (req.query.minimal_severity) {
|
|
478
|
+
opts.minimumSeverity = normalizeSeverity(
|
|
479
|
+
req.query.minimal_severity.toString()
|
|
480
|
+
);
|
|
481
|
+
}
|
|
361
482
|
const [notifications, totalCount] = await Promise.all([
|
|
362
483
|
store.getNotifications(opts),
|
|
363
484
|
store.getNotificationsCount(opts)
|
|
@@ -396,7 +517,7 @@ async function createRouter(options) {
|
|
|
396
517
|
await store.markRead({ user, ids });
|
|
397
518
|
if (signals) {
|
|
398
519
|
await signals.publish({
|
|
399
|
-
recipients: [user],
|
|
520
|
+
recipients: { type: "user", entityRef: [user] },
|
|
400
521
|
message: { action: "notification_read", notification_ids: ids },
|
|
401
522
|
channel: "notifications"
|
|
402
523
|
});
|
|
@@ -405,7 +526,7 @@ async function createRouter(options) {
|
|
|
405
526
|
await store.markUnread({ user, ids });
|
|
406
527
|
if (signals) {
|
|
407
528
|
await signals.publish({
|
|
408
|
-
recipients: [user],
|
|
529
|
+
recipients: { type: "user", entityRef: [user] },
|
|
409
530
|
message: { action: "notification_unread", notification_ids: ids },
|
|
410
531
|
channel: "notifications"
|
|
411
532
|
});
|
|
@@ -419,35 +540,49 @@ async function createRouter(options) {
|
|
|
419
540
|
const notifications = await store.getNotifications({ ids, user });
|
|
420
541
|
res.status(200).send(notifications);
|
|
421
542
|
});
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
543
|
+
const sendBroadcastNotification = async (baseNotification, opts) => {
|
|
544
|
+
const { scope, origin } = opts;
|
|
545
|
+
const broadcastNotification = {
|
|
546
|
+
...baseNotification,
|
|
547
|
+
id: uuid.v4()
|
|
548
|
+
};
|
|
549
|
+
const notification = await decorateNotification({
|
|
550
|
+
...broadcastNotification,
|
|
551
|
+
user: ""
|
|
552
|
+
});
|
|
553
|
+
let existingNotification;
|
|
554
|
+
if (scope) {
|
|
555
|
+
existingNotification = await store.getExistingScopeBroadcast({
|
|
556
|
+
scope,
|
|
557
|
+
origin
|
|
558
|
+
});
|
|
432
559
|
}
|
|
433
|
-
let
|
|
434
|
-
if (
|
|
435
|
-
|
|
560
|
+
let ret = notification;
|
|
561
|
+
if (existingNotification) {
|
|
562
|
+
const restored = await store.restoreExistingNotification({
|
|
563
|
+
id: existingNotification.id,
|
|
564
|
+
notification: { ...notification, user: "" }
|
|
565
|
+
});
|
|
566
|
+
ret = restored != null ? restored : notification;
|
|
567
|
+
} else {
|
|
568
|
+
await store.saveBroadcast(notification);
|
|
436
569
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
570
|
+
processorSendNotification(ret);
|
|
571
|
+
if (signals) {
|
|
572
|
+
await signals.publish({
|
|
573
|
+
recipients: { type: "broadcast" },
|
|
574
|
+
message: {
|
|
575
|
+
action: "new_notification",
|
|
576
|
+
notification_id: ret.id
|
|
577
|
+
},
|
|
578
|
+
channel: "notifications"
|
|
579
|
+
});
|
|
441
580
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
},
|
|
448
|
-
origin,
|
|
449
|
-
created: /* @__PURE__ */ new Date()
|
|
450
|
-
};
|
|
581
|
+
return notification;
|
|
582
|
+
};
|
|
583
|
+
const sendUserNotifications = async (baseNotification, users, opts) => {
|
|
584
|
+
const notifications = [];
|
|
585
|
+
const { scope, origin } = opts;
|
|
451
586
|
const uniqueUsers = [...new Set(users)];
|
|
452
587
|
for (const user of uniqueUsers) {
|
|
453
588
|
const userNotification = {
|
|
@@ -478,7 +613,7 @@ async function createRouter(options) {
|
|
|
478
613
|
notifications.push(ret);
|
|
479
614
|
if (signals) {
|
|
480
615
|
await signals.publish({
|
|
481
|
-
recipients: user,
|
|
616
|
+
recipients: { type: "user", entityRef: [user] },
|
|
482
617
|
message: {
|
|
483
618
|
action: "new_notification",
|
|
484
619
|
notification_id: ret.id
|
|
@@ -487,6 +622,48 @@ async function createRouter(options) {
|
|
|
487
622
|
});
|
|
488
623
|
}
|
|
489
624
|
}
|
|
625
|
+
return notifications;
|
|
626
|
+
};
|
|
627
|
+
router.post("/", async (req, res) => {
|
|
628
|
+
var _a;
|
|
629
|
+
const { recipients, payload } = req.body;
|
|
630
|
+
const notifications = [];
|
|
631
|
+
let users = [];
|
|
632
|
+
const credentials = await httpAuth.credentials(req, { allow: ["service"] });
|
|
633
|
+
const { title, scope } = payload;
|
|
634
|
+
if (!recipients || !title) {
|
|
635
|
+
logger.error(`Invalid notification request received`);
|
|
636
|
+
throw new errors.InputError();
|
|
637
|
+
}
|
|
638
|
+
const origin = credentials.principal.subject;
|
|
639
|
+
const baseNotification = {
|
|
640
|
+
payload: {
|
|
641
|
+
...payload,
|
|
642
|
+
severity: (_a = payload.severity) != null ? _a : "normal"
|
|
643
|
+
},
|
|
644
|
+
origin,
|
|
645
|
+
created: /* @__PURE__ */ new Date()
|
|
646
|
+
};
|
|
647
|
+
if (recipients.type === "broadcast") {
|
|
648
|
+
const broadcast = await sendBroadcastNotification(baseNotification, {
|
|
649
|
+
scope,
|
|
650
|
+
origin
|
|
651
|
+
});
|
|
652
|
+
notifications.push(broadcast);
|
|
653
|
+
} else {
|
|
654
|
+
const entityRef = recipients.entityRef;
|
|
655
|
+
try {
|
|
656
|
+
users = await getUsersForEntityRef(entityRef);
|
|
657
|
+
} catch (e) {
|
|
658
|
+
throw new errors.InputError();
|
|
659
|
+
}
|
|
660
|
+
const userNotifications = await sendUserNotifications(
|
|
661
|
+
baseNotification,
|
|
662
|
+
users,
|
|
663
|
+
{ scope, origin }
|
|
664
|
+
);
|
|
665
|
+
notifications.push(...userNotifications);
|
|
666
|
+
}
|
|
490
667
|
res.json(notifications);
|
|
491
668
|
});
|
|
492
669
|
router.use(backendCommon.errorHandler());
|
|
@@ -560,10 +737,14 @@ const notificationsPlugin = backendPluginApi.createBackendPlugin({
|
|
|
560
737
|
processors: processingExtensions.processors
|
|
561
738
|
})
|
|
562
739
|
);
|
|
740
|
+
httpRouter.addAuthPolicy({
|
|
741
|
+
path: "/health",
|
|
742
|
+
allow: "unauthenticated"
|
|
743
|
+
});
|
|
563
744
|
}
|
|
564
745
|
});
|
|
565
746
|
}
|
|
566
747
|
});
|
|
567
748
|
|
|
568
|
-
exports
|
|
749
|
+
exports.default = notificationsPlugin;
|
|
569
750
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/database/DatabaseNotificationsStore.ts","../src/service/router.ts","../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 */\nimport {\n PluginDatabaseManager,\n resolvePackagePath,\n} from '@backstage/backend-common';\nimport {\n NotificationGetOptions,\n NotificationModifyOptions,\n NotificationsStore,\n} from './NotificationsStore';\nimport { Notification } from '@backstage/plugin-notifications-common';\nimport { Knex } from 'knex';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-notifications-backend',\n 'migrations',\n);\n\n/** @internal */\nexport class DatabaseNotificationsStore implements NotificationsStore {\n private constructor(private readonly db: Knex) {}\n\n static async create({\n database,\n skipMigrations,\n }: {\n database: PluginDatabaseManager;\n skipMigrations?: boolean;\n }): Promise<DatabaseNotificationsStore> {\n const client = await database.getClient();\n\n if (!database.migrations?.skip && !skipMigrations) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseNotificationsStore(client);\n }\n\n private mapToInteger = (val: string | number | undefined): number => {\n return typeof val === 'string' ? Number.parseInt(val, 10) : val ?? 0;\n };\n\n private mapToNotifications = (rows: any[]): Notification[] => {\n return rows.map(row => ({\n id: row.id,\n user: row.user,\n created: new Date(row.created),\n saved: row.saved,\n read: row.read,\n updated: row.updated,\n origin: row.origin,\n payload: {\n title: row.title,\n description: row.description,\n link: row.link,\n topic: row.topic,\n severity: row.severity,\n scope: row.scope,\n icon: row.icon,\n },\n }));\n };\n\n private mapNotificationToDbRow = (notification: Notification) => {\n return {\n id: notification.id,\n user: notification.user,\n origin: notification.origin,\n created: notification.created,\n topic: notification.payload?.topic,\n link: notification.payload?.link,\n title: notification.payload?.title,\n description: notification.payload?.description,\n severity: notification.payload?.severity,\n scope: notification.payload?.scope,\n saved: notification.saved,\n read: notification.read,\n };\n };\n\n private getNotificationsBaseQuery = (\n options: NotificationGetOptions | NotificationModifyOptions,\n ) => {\n const { user } = options;\n const isSQLite = this.db.client.config.client.includes('sqlite3');\n\n const query = this.db('notification').where('user', user);\n\n if (options.sort !== undefined && options.sort !== null) {\n query.orderBy(options.sort, options.sortOrder ?? 'desc');\n } else if (options.sort !== null) {\n query.orderBy('created', options.sortOrder ?? 'desc');\n }\n\n if (options.createdAfter) {\n if (isSQLite) {\n query.where(\n 'notification.created',\n '>=',\n options.createdAfter.valueOf(),\n );\n } else {\n query.where(\n 'notification.created',\n '>=',\n options.createdAfter.toISOString(),\n );\n }\n }\n\n if (options.limit) {\n query.limit(options.limit);\n }\n\n if (options.offset) {\n query.offset(options.offset);\n }\n\n if (options.search) {\n query.whereRaw(\n `(LOWER(notification.title) LIKE LOWER(?) OR LOWER(notification.description) LIKE LOWER(?))`,\n [`%${options.search}%`, `%${options.search}%`],\n );\n }\n\n if (options.ids) {\n query.whereIn('notification.id', options.ids);\n }\n\n if (options.read) {\n query.whereNotNull('notification.read');\n } else if (options.read === false) {\n query.whereNull('notification.read');\n } // or match both if undefined\n\n if (options.saved) {\n query.whereNotNull('notification.saved');\n } else if (options.saved === false) {\n query.whereNull('notification.saved');\n } // or match both if undefined\n\n return query;\n };\n\n async getNotifications(options: NotificationGetOptions) {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n const notifications = await notificationQuery.select();\n return this.mapToNotifications(notifications);\n }\n\n async getNotificationsCount(options: NotificationGetOptions) {\n const countOptions: NotificationGetOptions = { ...options };\n countOptions.limit = undefined;\n countOptions.offset = undefined;\n countOptions.sort = null;\n const notificationQuery = this.getNotificationsBaseQuery(countOptions);\n const response = await notificationQuery.count('* as CNT');\n return Number(response[0].CNT);\n }\n\n async saveNotification(notification: Notification) {\n await this.db\n .insert(this.mapNotificationToDbRow(notification))\n .into('notification');\n }\n\n async getStatus(options: NotificationGetOptions) {\n const notificationQuery = this.getNotificationsBaseQuery({\n ...options,\n sort: null,\n });\n const readSubQuery = notificationQuery\n .clone()\n .count('id')\n .whereNotNull('read')\n .as('READ');\n const unreadSubQuery = notificationQuery\n .clone()\n .count('id')\n .whereNull('read')\n .as('UNREAD');\n\n const query = await notificationQuery\n .select(readSubQuery, unreadSubQuery)\n .first();\n\n return {\n unread: this.mapToInteger((query as any)?.UNREAD),\n read: this.mapToInteger((query as any)?.READ),\n };\n }\n\n async getExistingScopeNotification(options: {\n user: string;\n scope: string;\n origin: string;\n }) {\n const query = this.db('notification')\n .where('user', options.user)\n .where('scope', options.scope)\n .where('origin', options.origin)\n .select()\n .limit(1);\n\n const rows = await query;\n if (!rows || rows.length === 0) {\n return null;\n }\n return rows[0] as Notification;\n }\n\n async restoreExistingNotification(options: {\n id: string;\n notification: Notification;\n }) {\n const query = this.db('notification')\n .where('id', options.id)\n .where('user', options.notification.user);\n\n await query.update({\n title: options.notification.payload.title,\n description: options.notification.payload.description,\n link: options.notification.payload.link,\n topic: options.notification.payload.topic,\n updated: new Date(),\n severity: options.notification.payload.severity,\n read: null,\n });\n\n return await this.getNotification(options);\n }\n\n async getNotification(options: { id: string }): Promise<Notification | null> {\n const rows = await this.db('notification')\n .where('id', options.id)\n .select()\n .limit(1);\n if (!rows || rows.length === 0) {\n return null;\n }\n return this.mapToNotifications(rows)[0];\n }\n\n async markRead(options: NotificationModifyOptions): Promise<void> {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n await notificationQuery.update({ read: new Date() });\n }\n\n async markUnread(options: NotificationModifyOptions): Promise<void> {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n await notificationQuery.update({ read: null });\n }\n\n async markSaved(options: NotificationModifyOptions): Promise<void> {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n await notificationQuery.update({ saved: new Date() });\n }\n\n async markUnsaved(options: NotificationModifyOptions): Promise<void> {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n await notificationQuery.update({ saved: null });\n }\n}\n","/*\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 */\nimport { errorHandler, PluginDatabaseManager } from '@backstage/backend-common';\nimport express, { Request } from 'express';\nimport Router from 'express-promise-router';\nimport {\n DatabaseNotificationsStore,\n NotificationGetOptions,\n} from '../database';\nimport { v4 as uuid } from 'uuid';\nimport { CatalogApi, CatalogClient } from '@backstage/catalog-client';\nimport {\n Entity,\n isGroupEntity,\n isUserEntity,\n RELATION_HAS_MEMBER,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { NotificationProcessor } from '@backstage/plugin-notifications-node';\nimport { InputError } from '@backstage/errors';\nimport {\n AuthService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n NewNotificationSignal,\n Notification,\n NotificationReadSignal,\n} from '@backstage/plugin-notifications-common';\n\n/** @internal */\nexport interface RouterOptions {\n logger: LoggerService;\n database: PluginDatabaseManager;\n discovery: DiscoveryService;\n auth: AuthService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n signals?: SignalsService;\n catalog?: CatalogApi;\n processors?: NotificationProcessor[];\n}\n\nconst getSort = (input: string): NotificationGetOptions['sort'] | undefined => {\n const valid: NotificationGetOptions['sort'][] = [\n 'created',\n 'topic',\n 'origin',\n ];\n\n if ((valid as string[]).includes(input)) {\n return input as NotificationGetOptions['sort'];\n }\n return undefined;\n};\n\nconst getSortOrder = (input: string): NotificationGetOptions['sortOrder'] => {\n const valid: NotificationGetOptions['sortOrder'][] = ['asc', 'desc'];\n\n if ((valid as string[]).includes(input)) {\n return input as NotificationGetOptions['sortOrder'];\n }\n return 'desc';\n};\n\n/** @internal */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const {\n logger,\n database,\n auth,\n httpAuth,\n userInfo,\n discovery,\n catalog,\n processors,\n signals,\n } = options;\n\n const catalogClient =\n catalog ?? new CatalogClient({ discoveryApi: discovery });\n const store = await DatabaseNotificationsStore.create({ database });\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 getUsersForEntityRef = async (\n entityRef: string | string[] | null,\n ): Promise<string[]> => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n // TODO: Support for broadcast\n if (entityRef === null) {\n return [];\n }\n\n const refs = Array.isArray(entityRef) ? entityRef : [entityRef];\n const entities = await catalogClient.getEntitiesByRefs(\n {\n entityRefs: refs,\n fields: ['kind', 'metadata.name', 'metadata.namespace'],\n },\n { token },\n );\n const mapEntity = async (entity: Entity | undefined): Promise<string[]> => {\n if (!entity) {\n return [];\n }\n\n if (isUserEntity(entity)) {\n return [stringifyEntityRef(entity)];\n } else if (isGroupEntity(entity) && entity.relations) {\n const users = entity.relations\n .filter(\n relation =>\n relation.type === RELATION_HAS_MEMBER && relation.targetRef,\n )\n .map(r => r.targetRef);\n const childGroups = await catalogClient.getEntitiesByRefs(\n {\n entityRefs: entity.spec.children,\n fields: ['kind', 'metadata.name', 'metadata.namespace'],\n },\n { token },\n );\n const childGroupUsers = await Promise.all(\n childGroups.items.map(mapEntity),\n );\n return [...users, ...childGroupUsers.flat(2)];\n } else if (!isGroupEntity(entity) && entity.spec?.owner) {\n const owner = await catalogClient.getEntityByRef(\n entity.spec.owner as string,\n { token },\n );\n if (owner) {\n return mapEntity(owner);\n }\n }\n\n return [];\n };\n\n const users: string[] = [];\n for (const entity of entities.items) {\n const u = await mapEntity(entity);\n users.push(...u);\n }\n return users;\n };\n\n const decorateNotification = async (notification: Notification) => {\n let ret: Notification = notification;\n for (const processor of processors ?? []) {\n ret = processor.decorate ? await processor.decorate(ret) : ret;\n }\n return ret;\n };\n\n const processorSendNotification = async (notification: Notification) => {\n for (const processor of processors ?? []) {\n if (processor.send) {\n processor.send(notification);\n }\n }\n };\n\n // TODO: Move to use OpenAPI router instead\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n router.get('/', async (req, res) => {\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.sort) {\n opts.sort = getSort(req.query.sort.toString());\n }\n if (req.query.sort_order) {\n opts.sortOrder = getSortOrder(req.query.sort_order.toString());\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 if (req.query.created_after) {\n const sinceEpoch = Date.parse(String(req.query.created_after));\n if (isNaN(sinceEpoch)) {\n throw new InputError('Unexpected date format');\n }\n opts.createdAfter = new Date(sinceEpoch);\n }\n\n const [notifications, totalCount] = await Promise.all([\n store.getNotifications(opts),\n store.getNotificationsCount(opts),\n ]);\n res.send({\n totalCount,\n notifications,\n });\n });\n\n router.get('/:id', async (req, res) => {\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 res.status(404).send({ error: 'Not found' });\n return;\n }\n res.send(notifications[0]);\n });\n\n router.get('/status', async (req, res) => {\n const user = await getUser(req);\n const status = await store.getStatus({ user });\n res.send(status);\n });\n\n router.post('/update', async (req, res) => {\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: [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: [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.status(200).send(notifications);\n });\n\n // Add new notification\n router.post('/', async (req, res) => {\n const { recipients, payload } = req.body;\n const notifications = [];\n let users = [];\n\n const credentials = await httpAuth.credentials(req, { allow: ['service'] });\n\n const { title, scope } = payload;\n\n if (!recipients || !title) {\n logger.error(`Invalid notification request received`);\n throw new InputError();\n }\n\n let entityRef = null;\n // TODO: Support for broadcast notifications\n if (recipients.entityRef && recipients.type === 'entity') {\n entityRef = recipients.entityRef;\n }\n\n try {\n users = await getUsersForEntityRef(entityRef);\n } catch (e) {\n throw new InputError();\n }\n\n const origin = credentials.principal.subject;\n const baseNotification: Omit<Notification, 'id' | 'user'> = {\n payload: {\n ...payload,\n severity: payload.severity ?? 'normal',\n },\n origin,\n created: new Date(),\n };\n\n const uniqueUsers = [...new Set(users)];\n for (const user of uniqueUsers) {\n const userNotification = {\n ...baseNotification,\n id: uuid(),\n user,\n };\n const notification = await decorateNotification(userNotification);\n\n let existingNotification;\n if (scope) {\n existingNotification = await store.getExistingScopeNotification({\n user,\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,\n });\n ret = restored ?? notification;\n } else {\n await store.saveNotification(notification);\n }\n\n processorSendNotification(ret);\n notifications.push(ret);\n\n if (signals) {\n await signals.publish<NewNotificationSignal>({\n recipients: user,\n message: {\n action: 'new_notification',\n notification_id: ret.id,\n },\n channel: 'notifications',\n });\n }\n }\n\n res.json(notifications);\n });\n\n router.use(errorHandler());\n return router;\n}\n","/*\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';\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 discovery: coreServices.discovery,\n signals: signalsServiceRef,\n },\n async init({\n auth,\n httpAuth,\n userInfo,\n httpRouter,\n logger,\n database,\n discovery,\n signals,\n }) {\n httpRouter.use(\n await createRouter({\n auth,\n httpAuth,\n userInfo,\n logger,\n database,\n discovery,\n signals,\n processors: processingExtensions.processors,\n }),\n );\n },\n });\n },\n});\n"],"names":["resolvePackagePath","catalogClient","CatalogClient","isUserEntity","stringifyEntityRef","isGroupEntity","users","RELATION_HAS_MEMBER","Router","express","InputError","uuid","errorHandler","createBackendPlugin","notificationsProcessingExtensionPoint","coreServices","signalsServiceRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/database/DatabaseNotificationsStore.ts","../src/service/parseEntityOrderFieldParams.ts","../src/service/router.ts","../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 */\nimport {\n PluginDatabaseManager,\n resolvePackagePath,\n} from '@backstage/backend-common';\nimport {\n NotificationGetOptions,\n NotificationModifyOptions,\n NotificationsStore,\n} from './NotificationsStore';\nimport {\n Notification,\n NotificationSeverity,\n} from '@backstage/plugin-notifications-common';\nimport { Knex } from 'knex';\n\nconst migrationsDir = resolvePackagePath(\n '@backstage/plugin-notifications-backend',\n 'migrations',\n);\n\nconst NOTIFICATION_COLUMNS = [\n 'id',\n 'title',\n 'description',\n 'severity',\n 'link',\n 'origin',\n 'scope',\n 'topic',\n 'created',\n 'updated',\n 'user',\n 'read',\n 'saved',\n];\n\nconst severities: NotificationSeverity[] = [\n 'critical',\n 'high',\n 'normal',\n 'low',\n];\n\nexport const normalizeSeverity = (input?: string): NotificationSeverity => {\n let lower = (input ?? 'normal').toLowerCase() as NotificationSeverity;\n if (severities.indexOf(lower) < 0) {\n lower = 'normal';\n }\n return lower;\n};\n\n/** @internal */\nexport class DatabaseNotificationsStore implements NotificationsStore {\n private readonly isSQLite = false;\n\n private constructor(private readonly db: Knex) {\n this.isSQLite = this.db.client.config.client.includes('sqlite3');\n }\n\n static async create({\n database,\n skipMigrations,\n }: {\n database: PluginDatabaseManager;\n skipMigrations?: boolean;\n }): Promise<DatabaseNotificationsStore> {\n const client = await database.getClient();\n\n if (!database.migrations?.skip && !skipMigrations) {\n await client.migrate.latest({\n directory: migrationsDir,\n });\n }\n\n return new DatabaseNotificationsStore(client);\n }\n\n private mapToInteger = (val: string | number | undefined): number => {\n return typeof val === 'string' ? Number.parseInt(val, 10) : val ?? 0;\n };\n\n private mapToNotifications = (rows: any[]): Notification[] => {\n return rows.map(row => ({\n id: row.id,\n user: row.user,\n created: new Date(row.created),\n saved: row.saved,\n read: row.read,\n updated: row.updated,\n origin: row.origin,\n payload: {\n title: row.title,\n description: row.description,\n link: row.link,\n topic: row.topic,\n severity: row.severity,\n scope: row.scope,\n icon: row.icon,\n },\n }));\n };\n\n private mapNotificationToDbRow = (notification: Notification) => {\n return {\n id: notification.id,\n user: notification.user,\n origin: notification.origin,\n created: notification.created,\n topic: notification.payload?.topic,\n link: notification.payload?.link,\n title: notification.payload?.title,\n description: notification.payload?.description,\n severity: normalizeSeverity(notification.payload?.severity),\n scope: notification.payload?.scope,\n saved: notification.saved,\n read: notification.read,\n };\n };\n\n private mapBroadcastToDbRow = (notification: Notification) => {\n return {\n id: notification.id,\n origin: notification.origin,\n created: notification.created,\n topic: notification.payload?.topic,\n link: notification.payload?.link,\n title: notification.payload?.title,\n description: notification.payload?.description,\n severity: normalizeSeverity(notification.payload?.severity),\n scope: notification.payload?.scope,\n };\n };\n\n private getBroadcastUnion = () => {\n return this.db('broadcast')\n .leftJoin(\n 'broadcast_user_status',\n 'id',\n '=',\n 'broadcast_user_status.broadcast_id',\n )\n .select(NOTIFICATION_COLUMNS);\n };\n\n private getNotificationsBaseQuery = (\n options: NotificationGetOptions | NotificationModifyOptions,\n ) => {\n const { user, orderField } = options;\n\n const subQuery = this.db('notification')\n .select(NOTIFICATION_COLUMNS)\n .unionAll([this.getBroadcastUnion()])\n .as('notifications');\n\n const query = this.db.from(subQuery).where(q => {\n q.where('user', user).orWhereNull('user');\n });\n\n if (orderField && orderField.length > 0) {\n orderField.forEach(orderBy => {\n query.orderBy(orderBy.field, orderBy.order);\n });\n } else if (!orderField) {\n query.orderBy('created', 'desc');\n }\n\n if (options.createdAfter) {\n if (this.isSQLite) {\n query.where('created', '>=', options.createdAfter.valueOf());\n } else {\n query.where('created', '>=', options.createdAfter.toISOString());\n }\n }\n\n if (options.limit) {\n query.limit(options.limit);\n }\n\n if (options.offset) {\n query.offset(options.offset);\n }\n\n if (options.search) {\n query.whereRaw(\n `(LOWER(title) LIKE LOWER(?) OR LOWER(description) LIKE LOWER(?))`,\n [`%${options.search}%`, `%${options.search}%`],\n );\n }\n\n if (options.ids) {\n query.whereIn('id', options.ids);\n }\n\n if (options.read) {\n query.whereNotNull('read');\n } else if (options.read === false) {\n query.whereNull('read');\n } // or match both if undefined\n\n if (options.saved) {\n query.whereNotNull('saved');\n } else if (options.saved === false) {\n query.whereNull('saved');\n } // or match both if undefined\n\n if (options.minimumSeverity !== undefined) {\n const idx = severities.indexOf(options.minimumSeverity);\n const equalOrHigher = severities.slice(0, idx + 1);\n query.whereIn('severity', equalOrHigher);\n }\n\n return query;\n };\n\n async getNotifications(options: NotificationGetOptions) {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n const notifications = await notificationQuery.select(NOTIFICATION_COLUMNS);\n return this.mapToNotifications(notifications);\n }\n\n async getNotificationsCount(options: NotificationGetOptions) {\n const countOptions: NotificationGetOptions = { ...options };\n countOptions.limit = undefined;\n countOptions.offset = undefined;\n countOptions.orderField = [];\n const notificationQuery = this.getNotificationsBaseQuery(countOptions);\n const response = await notificationQuery.count('id as CNT');\n return Number(response[0].CNT);\n }\n\n async saveNotification(notification: Notification) {\n await this.db\n .insert(this.mapNotificationToDbRow(notification))\n .into('notification');\n }\n\n async saveBroadcast(notification: Notification) {\n await this.db\n .insert(this.mapBroadcastToDbRow(notification))\n .into('broadcast');\n if (notification.saved || notification.read) {\n await this.db\n .insert({\n user: notification.user,\n broadcast_id: notification.id,\n saved: notification.saved,\n read: notification.read,\n })\n .into('broadcast_user_status');\n }\n }\n\n async getStatus(options: NotificationGetOptions) {\n const notificationQuery = this.getNotificationsBaseQuery({\n ...options,\n orderField: [],\n });\n const readSubQuery = notificationQuery\n .clone()\n .count('id')\n .whereNotNull('read')\n .as('READ');\n const unreadSubQuery = notificationQuery\n .clone()\n .count('id')\n .whereNull('read')\n .as('UNREAD');\n\n const query = await notificationQuery\n .select(readSubQuery, unreadSubQuery)\n .first();\n\n return {\n unread: this.mapToInteger((query as any)?.UNREAD),\n read: this.mapToInteger((query as any)?.READ),\n };\n }\n\n async getExistingScopeNotification(options: {\n user: string;\n scope: string;\n origin: string;\n }) {\n const query = this.db('notification')\n .where('user', options.user)\n .where('scope', options.scope)\n .where('origin', options.origin)\n .limit(1);\n\n const rows = await query;\n if (!rows || rows.length === 0) {\n return null;\n }\n return rows[0] as Notification;\n }\n\n async getExistingScopeBroadcast(options: { scope: string; origin: string }) {\n const query = this.db('broadcast')\n .where('scope', options.scope)\n .where('origin', options.origin)\n .limit(1);\n\n const rows = await query;\n if (!rows || rows.length === 0) {\n return null;\n }\n return rows[0] as Notification;\n }\n\n async restoreExistingNotification({\n id,\n notification,\n }: {\n id: string;\n notification: Notification;\n }) {\n const updateColumns = {\n title: notification.payload.title,\n description: notification.payload.description,\n link: notification.payload.link,\n topic: notification.payload.topic,\n updated: new Date(),\n severity: normalizeSeverity(notification.payload?.severity),\n read: null,\n };\n\n const notificationQuery = this.db('notification')\n .where('id', id)\n .where('user', notification.user);\n const broadcastQuery = this.db('broadcast').where('id', id);\n\n await Promise.all([\n notificationQuery.update(updateColumns),\n broadcastQuery.update({ ...updateColumns, read: undefined }),\n ]);\n\n return await this.getNotification({ id });\n }\n\n async getNotification(options: { id: string }): Promise<Notification | null> {\n const rows = await this.db\n .select('*')\n .from(\n this.db('notification')\n .select(NOTIFICATION_COLUMNS)\n .unionAll([this.getBroadcastUnion()])\n .as('notifications'),\n )\n .where('id', options.id)\n .limit(1);\n if (!rows || rows.length === 0) {\n return null;\n }\n return this.mapToNotifications(rows)[0];\n }\n\n private markReadSaved = async (\n ids: string[],\n user: string,\n read?: Date | null,\n saved?: Date | null,\n ) => {\n await this.db('notification')\n .whereIn('id', ids)\n .where('user', user)\n .update({ read, saved });\n\n const broadcasts = this.mapToNotifications(\n await this.db('broadcast').whereIn('id', ids).select(),\n );\n\n if (broadcasts.length > 0)\n if (!this.isSQLite) {\n await this.db('broadcast_user_status')\n .insert(\n broadcasts.map(b => ({\n broadcast_id: b.id,\n user,\n read,\n saved,\n })),\n )\n .onConflict(['broadcast_id', 'user'])\n .merge(['read', 'saved']);\n } else {\n // SQLite does not support upsert so fall back to this (mostly for tests and local dev)\n for (const b of broadcasts) {\n const baseQuery = this.db('broadcast_user_status')\n .where('broadcast_id', b.id)\n .where('user', user);\n const exists = await baseQuery.clone().limit(1).select().first();\n if (exists) {\n await baseQuery.clone().update({ read, saved });\n } else {\n await baseQuery\n .clone()\n .insert({ broadcast_id: b.id, user, read, saved });\n }\n }\n }\n };\n\n async markRead(options: NotificationModifyOptions): Promise<void> {\n await this.markReadSaved(options.ids, options.user, new Date(), undefined);\n }\n\n async markUnread(options: NotificationModifyOptions): Promise<void> {\n await this.markReadSaved(options.ids, options.user, null, undefined);\n }\n\n async markSaved(options: NotificationModifyOptions): Promise<void> {\n await this.markReadSaved(options.ids, options.user, undefined, new Date());\n }\n\n async markUnsaved(options: NotificationModifyOptions): Promise<void> {\n await this.markReadSaved(options.ids, options.user, undefined, null);\n }\n}\n","/*\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\n// This file is based on the plugins/catalog-backend\n\nimport { InputError } from '@backstage/errors';\nimport { EntityOrder } from '../database';\n\n/**\n * Takes a single unknown parameter and makes sure that it's a single string or\n * an array of strings, and returns as an array.\n */\nexport function parseStringsParam(\n param: unknown,\n ctx: string,\n): string[] | undefined {\n if (param === undefined) {\n return undefined;\n }\n\n const array = [param].flat();\n if (array.some(p => typeof p !== 'string')) {\n throw new InputError(`Invalid ${ctx}, not a string`);\n }\n\n return array as string[];\n}\n\nexport function parseEntityOrderFieldParams(\n params: Record<string, unknown>,\n): EntityOrder[] | undefined {\n const orderFieldStrings = parseStringsParam(params.orderField, 'orderField');\n if (!orderFieldStrings) {\n return undefined;\n }\n\n return orderFieldStrings.map(orderFieldString => {\n const [field, order] = orderFieldString.split(',');\n\n if (order !== undefined && !isOrder(order)) {\n throw new InputError('Invalid order field order, must be asc or desc');\n }\n return { field, order };\n });\n}\n\nexport function isOrder(order: string): order is 'asc' | 'desc' {\n return ['asc', 'desc'].includes(order);\n}\n","/*\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 */\nimport { errorHandler, PluginDatabaseManager } from '@backstage/backend-common';\nimport express, { Request } from 'express';\nimport Router from 'express-promise-router';\nimport {\n DatabaseNotificationsStore,\n normalizeSeverity,\n NotificationGetOptions,\n} from '../database';\nimport { v4 as uuid } from 'uuid';\nimport { CatalogApi, CatalogClient } from '@backstage/catalog-client';\nimport {\n Entity,\n isGroupEntity,\n isUserEntity,\n RELATION_HAS_MEMBER,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { NotificationProcessor } from '@backstage/plugin-notifications-node';\nimport { InputError } from '@backstage/errors';\nimport {\n AuthService,\n DiscoveryService,\n HttpAuthService,\n LoggerService,\n UserInfoService,\n} from '@backstage/backend-plugin-api';\nimport { SignalsService } from '@backstage/plugin-signals-node';\nimport {\n NewNotificationSignal,\n Notification,\n NotificationReadSignal,\n} from '@backstage/plugin-notifications-common';\nimport { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams';\n\n/** @internal */\nexport interface RouterOptions {\n logger: LoggerService;\n database: PluginDatabaseManager;\n discovery: DiscoveryService;\n auth: AuthService;\n httpAuth: HttpAuthService;\n userInfo: UserInfoService;\n signals?: SignalsService;\n catalog?: CatalogApi;\n processors?: NotificationProcessor[];\n}\n\n/** @internal */\nexport async function createRouter(\n options: RouterOptions,\n): Promise<express.Router> {\n const {\n logger,\n database,\n auth,\n httpAuth,\n userInfo,\n discovery,\n catalog,\n processors,\n signals,\n } = options;\n\n const catalogClient =\n catalog ?? new CatalogClient({ discoveryApi: discovery });\n const store = await DatabaseNotificationsStore.create({ database });\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 getUsersForEntityRef = async (\n entityRef: string | string[] | null,\n ): Promise<string[]> => {\n const { token } = await auth.getPluginRequestToken({\n onBehalfOf: await auth.getOwnServiceCredentials(),\n targetPluginId: 'catalog',\n });\n\n if (entityRef === null) {\n return [];\n }\n\n const refs = Array.isArray(entityRef) ? entityRef : [entityRef];\n const entities = await catalogClient.getEntitiesByRefs(\n {\n entityRefs: refs,\n fields: ['kind', 'metadata.name', 'metadata.namespace'],\n },\n { token },\n );\n const mapEntity = async (entity: Entity | undefined): Promise<string[]> => {\n if (!entity) {\n return [];\n }\n\n if (isUserEntity(entity)) {\n return [stringifyEntityRef(entity)];\n } else if (isGroupEntity(entity) && entity.relations) {\n const users = entity.relations\n .filter(\n relation =>\n relation.type === RELATION_HAS_MEMBER && relation.targetRef,\n )\n .map(r => r.targetRef);\n const childGroups = await catalogClient.getEntitiesByRefs(\n {\n entityRefs: entity.spec.children,\n fields: ['kind', 'metadata.name', 'metadata.namespace'],\n },\n { token },\n );\n const childGroupUsers = await Promise.all(\n childGroups.items.map(mapEntity),\n );\n return [...users, ...childGroupUsers.flat(2)];\n } else if (!isGroupEntity(entity) && entity.spec?.owner) {\n const owner = await catalogClient.getEntityByRef(\n entity.spec.owner as string,\n { token },\n );\n if (owner) {\n return mapEntity(owner);\n }\n }\n\n return [];\n };\n\n const users: string[] = [];\n for (const entity of entities.items) {\n const u = await mapEntity(entity);\n users.push(...u);\n }\n return users;\n };\n\n const decorateNotification = async (notification: Notification) => {\n let ret = notification;\n for (const processor of processors ?? []) {\n ret = processor.decorate ? await processor.decorate(ret) : ret;\n }\n return ret;\n };\n\n const processorSendNotification = async (notification: Notification) => {\n for (const processor of processors ?? []) {\n if (processor.send) {\n processor.send(notification);\n }\n }\n };\n\n // TODO: Move to use OpenAPI router instead\n const router = Router();\n router.use(express.json());\n\n router.get('/health', (_, response) => {\n logger.info('PONG!');\n response.json({ status: 'ok' });\n });\n\n router.get('/', async (req, res) => {\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 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 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.minimal_severity) {\n opts.minimumSeverity = normalizeSeverity(\n req.query.minimal_severity.toString(),\n );\n }\n\n const [notifications, totalCount] = await Promise.all([\n store.getNotifications(opts),\n store.getNotificationsCount(opts),\n ]);\n res.send({\n totalCount,\n notifications,\n });\n });\n\n router.get('/:id', async (req, res) => {\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 res.status(404).send({ error: 'Not found' });\n return;\n }\n res.send(notifications[0]);\n });\n\n router.get('/status', async (req, res) => {\n const user = await getUser(req);\n const status = await store.getStatus({ user });\n res.send(status);\n });\n\n router.post('/update', async (req, res) => {\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.status(200).send(notifications);\n });\n\n const sendBroadcastNotification = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n opts: { scope?: string; origin: string },\n ) => {\n const { scope, origin } = opts;\n const broadcastNotification = {\n ...baseNotification,\n id: uuid(),\n };\n const notification = await decorateNotification({\n ...broadcastNotification,\n user: '',\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 processorSendNotification(ret);\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 return notification;\n };\n\n const sendUserNotifications = async (\n baseNotification: Omit<Notification, 'user' | 'id'>,\n users: string[],\n opts: { scope?: string; origin: string },\n ) => {\n const notifications = [];\n const { scope, origin } = opts;\n const uniqueUsers = [...new Set(users)];\n for (const user of uniqueUsers) {\n const userNotification = {\n ...baseNotification,\n id: uuid(),\n user,\n };\n const notification = await decorateNotification(userNotification);\n\n let existingNotification;\n if (scope) {\n existingNotification = await store.getExistingScopeNotification({\n user,\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,\n });\n ret = restored ?? notification;\n } else {\n await store.saveNotification(notification);\n }\n\n processorSendNotification(ret);\n notifications.push(ret);\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 }\n return notifications;\n };\n\n // Add new notification\n router.post('/', async (req, res) => {\n const { recipients, payload } = req.body;\n const notifications = [];\n let users = [];\n\n const credentials = await httpAuth.credentials(req, { allow: ['service'] });\n\n const { title, scope } = payload;\n\n if (!recipients || !title) {\n logger.error(`Invalid notification request received`);\n throw new InputError();\n }\n\n const origin = credentials.principal.subject;\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(baseNotification, {\n scope,\n origin,\n });\n notifications.push(broadcast);\n } else {\n const entityRef = recipients.entityRef;\n\n try {\n users = await getUsersForEntityRef(entityRef);\n } catch (e) {\n throw new InputError();\n }\n const userNotifications = await sendUserNotifications(\n baseNotification,\n users,\n { scope, origin },\n );\n notifications.push(...userNotifications);\n }\n\n res.json(notifications);\n });\n\n router.use(errorHandler());\n return router;\n}\n","/*\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';\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 discovery: coreServices.discovery,\n signals: signalsServiceRef,\n },\n async init({\n auth,\n httpAuth,\n userInfo,\n httpRouter,\n logger,\n database,\n discovery,\n signals,\n }) {\n httpRouter.use(\n await createRouter({\n auth,\n httpAuth,\n userInfo,\n logger,\n database,\n discovery,\n signals,\n processors: processingExtensions.processors,\n }),\n );\n httpRouter.addAuthPolicy({\n path: '/health',\n allow: 'unauthenticated',\n });\n },\n });\n },\n});\n"],"names":["resolvePackagePath","InputError","catalogClient","CatalogClient","isUserEntity","stringifyEntityRef","isGroupEntity","users","RELATION_HAS_MEMBER","Router","express","uuid","errorHandler","createBackendPlugin","notificationsProcessingExtensionPoint","coreServices","signalsServiceRef"],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
await knex.schema.createTable('notification', table => {
|
|
19
|
+
table.uuid('id').primary();
|
|
20
|
+
table.string('user', 255).notNullable();
|
|
21
|
+
table.string('title').notNullable();
|
|
22
|
+
table.text('description').nullable();
|
|
23
|
+
table.string('severity', 8).notNullable();
|
|
24
|
+
table.text('link').notNullable();
|
|
25
|
+
table.string('origin', 255).notNullable();
|
|
26
|
+
table.string('scope', 255).nullable();
|
|
27
|
+
table.string('topic', 255).nullable();
|
|
28
|
+
table.datetime('created').defaultTo(knex.fn.now()).notNullable();
|
|
29
|
+
table.datetime('updated').nullable();
|
|
30
|
+
table.datetime('read').nullable();
|
|
31
|
+
table.datetime('done').nullable();
|
|
32
|
+
table.datetime('saved').nullable();
|
|
33
|
+
|
|
34
|
+
table.index(['user'], 'notification_user_idx');
|
|
35
|
+
table.index(['scope', 'origin'], 'notification_scope_origin_idx');
|
|
36
|
+
});
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {import('knex').Knex} knex
|
|
41
|
+
*/
|
|
42
|
+
exports.down = async function down(knex) {
|
|
43
|
+
await knex.schema.dropTable('notification');
|
|
44
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
await knex.schema.alterTable('notification', table => {
|
|
19
|
+
table.text('link').nullable().alter();
|
|
20
|
+
table.dropColumn('done');
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
exports.down = async function down(knex) {
|
|
25
|
+
await knex.schema.alterTable('notification', table => {
|
|
26
|
+
table.text('link').notNullable().alter();
|
|
27
|
+
table.datetime('done').nullable();
|
|
28
|
+
});
|
|
29
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2024 The Backstage Authors
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
exports.up = async function up(knex) {
|
|
18
|
+
await knex.schema.createTable('broadcast', table => {
|
|
19
|
+
table.uuid('id').primary();
|
|
20
|
+
table.string('title').notNullable();
|
|
21
|
+
table.text('description').nullable();
|
|
22
|
+
table.string('severity', 8).notNullable();
|
|
23
|
+
table.text('link').nullable();
|
|
24
|
+
table.string('origin', 255).notNullable();
|
|
25
|
+
table.string('scope', 255).nullable();
|
|
26
|
+
table.string('topic', 255).nullable();
|
|
27
|
+
table.datetime('created').defaultTo(knex.fn.now()).notNullable();
|
|
28
|
+
table.datetime('updated').nullable();
|
|
29
|
+
table.index(['scope', 'origin'], 'broadcast_cope_origin_idx');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await knex.schema.createTable('broadcast_user_status', table => {
|
|
33
|
+
table.uuid('broadcast_id').notNullable();
|
|
34
|
+
table.string('user', 255).notNullable();
|
|
35
|
+
table.datetime('read').nullable();
|
|
36
|
+
table.datetime('saved').nullable();
|
|
37
|
+
|
|
38
|
+
table
|
|
39
|
+
.foreign('broadcast_id')
|
|
40
|
+
.references(['id'])
|
|
41
|
+
.inTable('broadcast')
|
|
42
|
+
.onDelete('CASCADE');
|
|
43
|
+
table.unique(['broadcast_id', 'user'], {
|
|
44
|
+
indexName: 'broadcast_user_idx',
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {import('knex').Knex} knex
|
|
51
|
+
*/
|
|
52
|
+
exports.down = async function down(knex) {
|
|
53
|
+
await knex.schema.dropTable('broadcast_user_status');
|
|
54
|
+
await knex.schema.dropTable('broadcast');
|
|
55
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-notifications-backend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"backstage": {
|
|
5
5
|
"role": "backend-plugin"
|
|
6
6
|
},
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"main": "dist/index.cjs.js",
|
|
19
19
|
"types": "dist/index.d.ts",
|
|
20
20
|
"files": [
|
|
21
|
-
"dist"
|
|
21
|
+
"dist",
|
|
22
|
+
"migrations/**/*.{js,d.ts}"
|
|
22
23
|
],
|
|
23
24
|
"scripts": {
|
|
24
25
|
"build": "backstage-cli package build",
|
|
@@ -30,17 +31,17 @@
|
|
|
30
31
|
"test": "backstage-cli package test"
|
|
31
32
|
},
|
|
32
33
|
"dependencies": {
|
|
33
|
-
"@backstage/backend-common": "^0.21.
|
|
34
|
-
"@backstage/backend-plugin-api": "^0.6.
|
|
35
|
-
"@backstage/catalog-client": "^1.6.
|
|
36
|
-
"@backstage/catalog-model": "^1.4.5
|
|
37
|
-
"@backstage/config": "^1.2.0
|
|
38
|
-
"@backstage/errors": "^1.2.4
|
|
39
|
-
"@backstage/plugin-auth-node": "^0.4.
|
|
40
|
-
"@backstage/plugin-events-node": "^0.3.
|
|
41
|
-
"@backstage/plugin-notifications-common": "^0.0.2
|
|
42
|
-
"@backstage/plugin-notifications-node": "^0.1.
|
|
43
|
-
"@backstage/plugin-signals-node": "^0.1.
|
|
34
|
+
"@backstage/backend-common": "^0.21.5",
|
|
35
|
+
"@backstage/backend-plugin-api": "^0.6.15",
|
|
36
|
+
"@backstage/catalog-client": "^1.6.2",
|
|
37
|
+
"@backstage/catalog-model": "^1.4.5",
|
|
38
|
+
"@backstage/config": "^1.2.0",
|
|
39
|
+
"@backstage/errors": "^1.2.4",
|
|
40
|
+
"@backstage/plugin-auth-node": "^0.4.10",
|
|
41
|
+
"@backstage/plugin-events-node": "^0.3.1",
|
|
42
|
+
"@backstage/plugin-notifications-common": "^0.0.2",
|
|
43
|
+
"@backstage/plugin-notifications-node": "^0.1.1",
|
|
44
|
+
"@backstage/plugin-signals-node": "^0.1.1",
|
|
44
45
|
"express": "^4.17.1",
|
|
45
46
|
"express-promise-router": "^4.1.0",
|
|
46
47
|
"knex": "^3.0.0",
|
|
@@ -50,8 +51,8 @@
|
|
|
50
51
|
"yn": "^4.0.0"
|
|
51
52
|
},
|
|
52
53
|
"devDependencies": {
|
|
53
|
-
"@backstage/backend-test-utils": "^0.3.
|
|
54
|
-
"@backstage/cli": "^0.
|
|
54
|
+
"@backstage/backend-test-utils": "^0.3.5",
|
|
55
|
+
"@backstage/cli": "^0.26.1",
|
|
55
56
|
"@types/express": "^4.17.6",
|
|
56
57
|
"@types/supertest": "^2.0.8",
|
|
57
58
|
"msw": "^1.0.0",
|