@backstage/plugin-notifications-backend 0.0.1-next.0
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 +19 -0
- package/README.md +77 -0
- package/dist/index.cjs.js +508 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/package.json +55 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# @backstage/plugin-notifications-backend
|
|
2
|
+
|
|
3
|
+
## 0.0.1-next.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- fb8fc24: Initial notifications system for backstage
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @backstage/backend-common@0.21.0-next.2
|
|
10
|
+
- @backstage/plugin-signals-node@0.0.1-next.2
|
|
11
|
+
- @backstage/backend-plugin-api@0.6.10-next.2
|
|
12
|
+
- @backstage/plugin-auth-node@0.4.4-next.2
|
|
13
|
+
- @backstage/plugin-notifications-common@0.0.1-next.0
|
|
14
|
+
- @backstage/plugin-notifications-node@0.0.1-next.0
|
|
15
|
+
- @backstage/plugin-events-node@0.2.19-next.2
|
|
16
|
+
- @backstage/config@1.1.1
|
|
17
|
+
- @backstage/catalog-client@1.6.0-next.1
|
|
18
|
+
- @backstage/catalog-model@1.4.4-next.0
|
|
19
|
+
- @backstage/errors@1.2.3
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# notifications
|
|
2
|
+
|
|
3
|
+
Welcome to the notifications backend plugin!
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
Add the notifications to your backend:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const backend = createBackend();
|
|
11
|
+
// ...
|
|
12
|
+
backend.add(import('@backstage/plugin-notifications-backend'));
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
For users to be able to see notifications in real-time, you have to install also
|
|
16
|
+
the signals plugin (`@backstage/plugin-signals-node`, `@backstage/plugin-signals-backend`, and
|
|
17
|
+
`@backstage/plugin-signals`).
|
|
18
|
+
|
|
19
|
+
## Extending Notifications
|
|
20
|
+
|
|
21
|
+
The notifications can be extended with `NotificationProcessor`. These processors allow to decorate notifications
|
|
22
|
+
before they are sent or/and send the notifications to external services.
|
|
23
|
+
|
|
24
|
+
Start off by creating a notification processor:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { Notification } from '@backstage/plugin-notifications-common';
|
|
28
|
+
import { NotificationProcessor } from '@backstage/plugin-notifications-node';
|
|
29
|
+
|
|
30
|
+
class MyNotificationProcessor implements NotificationProcessor {
|
|
31
|
+
async decorate(notification: Notification): Promise<Notification> {
|
|
32
|
+
if (notification.origin === 'plugin-my-plugin') {
|
|
33
|
+
notification.payload.icon = 'my-icon';
|
|
34
|
+
}
|
|
35
|
+
return notification;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async send(notification: Notification): Promise<void> {
|
|
39
|
+
nodemailer.sendEmail({
|
|
40
|
+
from: 'backstage',
|
|
41
|
+
to: 'user',
|
|
42
|
+
subject: notification.payload.title,
|
|
43
|
+
text: notification.payload.description,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Both of the processing functions are optional, and you can implement only one of them.
|
|
50
|
+
|
|
51
|
+
Add the notification processor to the notification system by:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { notificationsProcessingExtensionPoint } from '@backstage/plugin-notifications-node';
|
|
55
|
+
import { Notification } from '@backstage/plugin-notifications-common';
|
|
56
|
+
|
|
57
|
+
export const myPlugin = createBackendPlugin({
|
|
58
|
+
pluginId: 'myPlugin',
|
|
59
|
+
register(env) {
|
|
60
|
+
env.registerInit({
|
|
61
|
+
deps: {
|
|
62
|
+
notifications: notificationsProcessingExtensionPoint,
|
|
63
|
+
// ...
|
|
64
|
+
},
|
|
65
|
+
async init({ notifications }) {
|
|
66
|
+
// ...
|
|
67
|
+
notifications.addProcessor(new MyNotificationProcessor());
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Sending notifications
|
|
75
|
+
|
|
76
|
+
To be able to send notifications to users, you have to integrate the `@backstage/plugin-notifications-node`
|
|
77
|
+
to your application and plugins.
|
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var backendPluginApi = require('@backstage/backend-plugin-api');
|
|
6
|
+
var backendCommon = require('@backstage/backend-common');
|
|
7
|
+
var express = require('express');
|
|
8
|
+
var Router = require('express-promise-router');
|
|
9
|
+
var pluginAuthNode = require('@backstage/plugin-auth-node');
|
|
10
|
+
var uuid = require('uuid');
|
|
11
|
+
var catalogClient = require('@backstage/catalog-client');
|
|
12
|
+
var catalogModel = require('@backstage/catalog-model');
|
|
13
|
+
var errors = require('@backstage/errors');
|
|
14
|
+
var pluginSignalsNode = require('@backstage/plugin-signals-node');
|
|
15
|
+
var pluginNotificationsNode = require('@backstage/plugin-notifications-node');
|
|
16
|
+
|
|
17
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
18
|
+
|
|
19
|
+
var express__default = /*#__PURE__*/_interopDefaultLegacy(express);
|
|
20
|
+
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
21
|
+
|
|
22
|
+
var __defProp = Object.defineProperty;
|
|
23
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
24
|
+
var __publicField = (obj, key, value) => {
|
|
25
|
+
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
const migrationsDir = backendCommon.resolvePackagePath(
|
|
29
|
+
"@backstage/plugin-notifications-backend",
|
|
30
|
+
"migrations"
|
|
31
|
+
);
|
|
32
|
+
class DatabaseNotificationsStore {
|
|
33
|
+
constructor(db) {
|
|
34
|
+
this.db = db;
|
|
35
|
+
__publicField(this, "mapToInteger", (val) => {
|
|
36
|
+
return typeof val === "string" ? Number.parseInt(val, 10) : val != null ? val : 0;
|
|
37
|
+
});
|
|
38
|
+
__publicField(this, "mapToNotifications", (rows) => {
|
|
39
|
+
return rows.map((row) => ({
|
|
40
|
+
id: row.id,
|
|
41
|
+
user: row.user,
|
|
42
|
+
created: row.created,
|
|
43
|
+
done: row.done,
|
|
44
|
+
saved: row.saved,
|
|
45
|
+
read: row.read,
|
|
46
|
+
updated: row.updated,
|
|
47
|
+
origin: row.origin,
|
|
48
|
+
payload: {
|
|
49
|
+
title: row.title,
|
|
50
|
+
description: row.description,
|
|
51
|
+
link: row.link,
|
|
52
|
+
topic: row.topic,
|
|
53
|
+
severity: row.severity,
|
|
54
|
+
scope: row.scope,
|
|
55
|
+
icon: row.icon
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
58
|
+
});
|
|
59
|
+
__publicField(this, "getNotificationsBaseQuery", (options) => {
|
|
60
|
+
var _a, _b;
|
|
61
|
+
const { user, type } = options;
|
|
62
|
+
const query = this.db("notification").where("user", user);
|
|
63
|
+
if (options.sort !== void 0 && options.sort !== null) {
|
|
64
|
+
query.orderBy(options.sort, (_a = options.sortOrder) != null ? _a : "desc");
|
|
65
|
+
} else if (options.sort !== null) {
|
|
66
|
+
query.orderBy("created", (_b = options.sortOrder) != null ? _b : "desc");
|
|
67
|
+
}
|
|
68
|
+
if (type === "undone") {
|
|
69
|
+
query.whereNull("done");
|
|
70
|
+
} else if (type === "done") {
|
|
71
|
+
query.whereNotNull("done");
|
|
72
|
+
} else if (type === "saved") {
|
|
73
|
+
query.whereNotNull("saved");
|
|
74
|
+
}
|
|
75
|
+
if (options.limit) {
|
|
76
|
+
query.limit(options.limit);
|
|
77
|
+
}
|
|
78
|
+
if (options.offset) {
|
|
79
|
+
query.offset(options.offset);
|
|
80
|
+
}
|
|
81
|
+
if (options.search) {
|
|
82
|
+
query.whereRaw(
|
|
83
|
+
`(LOWER(notification.title) LIKE LOWER(?) OR LOWER(notification.description) LIKE LOWER(?))`,
|
|
84
|
+
[`%${options.search}%`, `%${options.search}%`]
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (options.ids) {
|
|
88
|
+
query.whereIn("notification.id", options.ids);
|
|
89
|
+
}
|
|
90
|
+
return query;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
static async create({
|
|
94
|
+
database,
|
|
95
|
+
skipMigrations
|
|
96
|
+
}) {
|
|
97
|
+
var _a;
|
|
98
|
+
const client = await database.getClient();
|
|
99
|
+
if (!((_a = database.migrations) == null ? void 0 : _a.skip) && !skipMigrations) {
|
|
100
|
+
await client.migrate.latest({
|
|
101
|
+
directory: migrationsDir
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return new DatabaseNotificationsStore(client);
|
|
105
|
+
}
|
|
106
|
+
async getNotifications(options) {
|
|
107
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
108
|
+
const notifications = await notificationQuery.select();
|
|
109
|
+
return this.mapToNotifications(notifications);
|
|
110
|
+
}
|
|
111
|
+
async saveNotification(notification) {
|
|
112
|
+
await this.db.insert(notification).into("notification");
|
|
113
|
+
}
|
|
114
|
+
async getStatus(options) {
|
|
115
|
+
const notificationQuery = this.getNotificationsBaseQuery({
|
|
116
|
+
...options,
|
|
117
|
+
sort: null
|
|
118
|
+
});
|
|
119
|
+
const readSubQuery = notificationQuery.clone().count("id").whereNotNull("read").as("READ");
|
|
120
|
+
const unreadSubQuery = notificationQuery.clone().count("id").whereNull("read").as("UNREAD");
|
|
121
|
+
const query = await notificationQuery.select(readSubQuery, unreadSubQuery).first();
|
|
122
|
+
return {
|
|
123
|
+
unread: this.mapToInteger(query == null ? void 0 : query.UNREAD),
|
|
124
|
+
read: this.mapToInteger(query == null ? void 0 : query.READ)
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
async getExistingScopeNotification(options) {
|
|
128
|
+
const query = this.db("notification").where("user", options.user).where("scope", options.scope).where("origin", options.origin).select().limit(1);
|
|
129
|
+
const rows = await query;
|
|
130
|
+
if (!rows || rows.length === 0) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return rows[0];
|
|
134
|
+
}
|
|
135
|
+
async restoreExistingNotification(options) {
|
|
136
|
+
const query = this.db("notification").where("id", options.id).where("user", options.notification.user);
|
|
137
|
+
await query.update({
|
|
138
|
+
title: options.notification.payload.title,
|
|
139
|
+
description: options.notification.payload.description,
|
|
140
|
+
link: options.notification.payload.link,
|
|
141
|
+
topic: options.notification.payload.topic,
|
|
142
|
+
updated: options.notification.created,
|
|
143
|
+
severity: options.notification.payload.severity,
|
|
144
|
+
read: null,
|
|
145
|
+
done: null
|
|
146
|
+
});
|
|
147
|
+
return await this.getNotification(options);
|
|
148
|
+
}
|
|
149
|
+
async getNotification(options) {
|
|
150
|
+
const rows = await this.db("notification").where("id", options.id).select().limit(1);
|
|
151
|
+
if (!rows || rows.length === 0) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
return this.mapToNotifications(rows)[0];
|
|
155
|
+
}
|
|
156
|
+
async markRead(options) {
|
|
157
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
158
|
+
await notificationQuery.update({ read: /* @__PURE__ */ new Date() });
|
|
159
|
+
}
|
|
160
|
+
async markUnread(options) {
|
|
161
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
162
|
+
await notificationQuery.update({ read: null });
|
|
163
|
+
}
|
|
164
|
+
async markDone(options) {
|
|
165
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
166
|
+
await notificationQuery.update({ done: /* @__PURE__ */ new Date(), read: /* @__PURE__ */ new Date() });
|
|
167
|
+
}
|
|
168
|
+
async markUndone(options) {
|
|
169
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
170
|
+
await notificationQuery.update({ done: null, read: null });
|
|
171
|
+
}
|
|
172
|
+
async markSaved(options) {
|
|
173
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
174
|
+
await notificationQuery.update({ saved: /* @__PURE__ */ new Date() });
|
|
175
|
+
}
|
|
176
|
+
async markUnsaved(options) {
|
|
177
|
+
const notificationQuery = this.getNotificationsBaseQuery(options);
|
|
178
|
+
await notificationQuery.update({ saved: null });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function createRouter(options) {
|
|
183
|
+
const {
|
|
184
|
+
logger,
|
|
185
|
+
database,
|
|
186
|
+
identity,
|
|
187
|
+
discovery,
|
|
188
|
+
catalog,
|
|
189
|
+
tokenManager,
|
|
190
|
+
processors,
|
|
191
|
+
signalService
|
|
192
|
+
} = options;
|
|
193
|
+
const catalogClient$1 = catalog != null ? catalog : new catalogClient.CatalogClient({ discoveryApi: discovery });
|
|
194
|
+
const store = await DatabaseNotificationsStore.create({ database });
|
|
195
|
+
const getUser = async (req) => {
|
|
196
|
+
const user = await identity.getIdentity({ request: req });
|
|
197
|
+
if (!user) {
|
|
198
|
+
throw new errors.AuthenticationError();
|
|
199
|
+
}
|
|
200
|
+
return user.identity.userEntityRef;
|
|
201
|
+
};
|
|
202
|
+
const authenticateService = async (req) => {
|
|
203
|
+
const token = pluginAuthNode.getBearerTokenFromAuthorizationHeader(
|
|
204
|
+
req.header("authorization")
|
|
205
|
+
);
|
|
206
|
+
if (!token) {
|
|
207
|
+
throw new errors.AuthenticationError();
|
|
208
|
+
}
|
|
209
|
+
await tokenManager.authenticate(token);
|
|
210
|
+
};
|
|
211
|
+
const getUsersForEntityRef = async (entityRef) => {
|
|
212
|
+
const { token } = await tokenManager.getToken();
|
|
213
|
+
if (entityRef === null) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
const refs = Array.isArray(entityRef) ? entityRef : [entityRef];
|
|
217
|
+
const entities = await catalogClient$1.getEntitiesByRefs(
|
|
218
|
+
{
|
|
219
|
+
entityRefs: refs,
|
|
220
|
+
fields: ["kind", "metadata.name", "metadata.namespace"]
|
|
221
|
+
},
|
|
222
|
+
{ token }
|
|
223
|
+
);
|
|
224
|
+
const mapEntity = async (entity) => {
|
|
225
|
+
var _a;
|
|
226
|
+
if (!entity) {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
if (catalogModel.isUserEntity(entity)) {
|
|
230
|
+
return [catalogModel.stringifyEntityRef(entity)];
|
|
231
|
+
} else if (catalogModel.isGroupEntity(entity) && entity.relations) {
|
|
232
|
+
const users2 = entity.relations.filter(
|
|
233
|
+
(relation) => relation.type === catalogModel.RELATION_HAS_MEMBER && relation.targetRef
|
|
234
|
+
).map((r) => r.targetRef);
|
|
235
|
+
const childGroups = await catalogClient$1.getEntitiesByRefs(
|
|
236
|
+
{
|
|
237
|
+
entityRefs: entity.spec.children,
|
|
238
|
+
fields: ["kind", "metadata.name", "metadata.namespace"]
|
|
239
|
+
},
|
|
240
|
+
{ token }
|
|
241
|
+
);
|
|
242
|
+
const childGroupUsers = await Promise.all(
|
|
243
|
+
childGroups.items.map(mapEntity)
|
|
244
|
+
);
|
|
245
|
+
return [...users2, ...childGroupUsers.flat(2)];
|
|
246
|
+
} else if (!catalogModel.isGroupEntity(entity) && ((_a = entity.spec) == null ? void 0 : _a.owner)) {
|
|
247
|
+
const owner = await catalogClient$1.getEntityByRef(
|
|
248
|
+
entity.spec.owner,
|
|
249
|
+
{ token }
|
|
250
|
+
);
|
|
251
|
+
if (owner) {
|
|
252
|
+
return mapEntity(owner);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return [];
|
|
256
|
+
};
|
|
257
|
+
const users = [];
|
|
258
|
+
for (const entity of entities.items) {
|
|
259
|
+
const u = await mapEntity(entity);
|
|
260
|
+
users.push(...u);
|
|
261
|
+
}
|
|
262
|
+
return users;
|
|
263
|
+
};
|
|
264
|
+
const decorateNotification = async (notification) => {
|
|
265
|
+
let ret = notification;
|
|
266
|
+
for (const processor of processors != null ? processors : []) {
|
|
267
|
+
ret = processor.decorate ? await processor.decorate(ret) : ret;
|
|
268
|
+
}
|
|
269
|
+
return ret;
|
|
270
|
+
};
|
|
271
|
+
const processorSendNotification = async (notification) => {
|
|
272
|
+
for (const processor of processors != null ? processors : []) {
|
|
273
|
+
if (processor.send) {
|
|
274
|
+
processor.send(notification);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
const router = Router__default["default"]();
|
|
279
|
+
router.use(express__default["default"].json());
|
|
280
|
+
router.get("/health", (_, response) => {
|
|
281
|
+
logger.info("PONG!");
|
|
282
|
+
response.json({ status: "ok" });
|
|
283
|
+
});
|
|
284
|
+
router.get("/", async (req, res) => {
|
|
285
|
+
const user = await getUser(req);
|
|
286
|
+
const opts = {
|
|
287
|
+
user
|
|
288
|
+
};
|
|
289
|
+
if (req.query.type) {
|
|
290
|
+
opts.type = req.query.type.toString();
|
|
291
|
+
}
|
|
292
|
+
if (req.query.offset) {
|
|
293
|
+
opts.offset = Number.parseInt(req.query.offset.toString(), 10);
|
|
294
|
+
}
|
|
295
|
+
if (req.query.limit) {
|
|
296
|
+
opts.limit = Number.parseInt(req.query.limit.toString(), 10);
|
|
297
|
+
}
|
|
298
|
+
if (req.query.search) {
|
|
299
|
+
opts.search = req.query.search.toString();
|
|
300
|
+
}
|
|
301
|
+
const notifications = await store.getNotifications(opts);
|
|
302
|
+
res.send(notifications);
|
|
303
|
+
});
|
|
304
|
+
router.get("/status", async (req, res) => {
|
|
305
|
+
const user = await getUser(req);
|
|
306
|
+
const status = await store.getStatus({ user, type: "undone" });
|
|
307
|
+
res.send(status);
|
|
308
|
+
});
|
|
309
|
+
router.post("/update", async (req, res) => {
|
|
310
|
+
const user = await getUser(req);
|
|
311
|
+
const { ids, done, read, saved } = req.body;
|
|
312
|
+
if (!ids || !Array.isArray(ids)) {
|
|
313
|
+
throw new errors.InputError();
|
|
314
|
+
}
|
|
315
|
+
if (done === true) {
|
|
316
|
+
await store.markDone({ user, ids });
|
|
317
|
+
if (signalService) {
|
|
318
|
+
await signalService.publish({
|
|
319
|
+
recipients: [user],
|
|
320
|
+
message: { action: "done", notification_ids: ids },
|
|
321
|
+
channel: "notifications"
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
} else if (done === false) {
|
|
325
|
+
await store.markUndone({ user, ids });
|
|
326
|
+
if (signalService) {
|
|
327
|
+
await signalService.publish({
|
|
328
|
+
recipients: [user],
|
|
329
|
+
message: { action: "undone", notification_ids: ids },
|
|
330
|
+
channel: "notifications"
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (read === true) {
|
|
335
|
+
await store.markRead({ user, ids });
|
|
336
|
+
if (signalService) {
|
|
337
|
+
await signalService.publish({
|
|
338
|
+
recipients: [user],
|
|
339
|
+
message: { action: "mark_read", notification_ids: ids },
|
|
340
|
+
channel: "notifications"
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
} else if (read === false) {
|
|
344
|
+
await store.markUnread({ user, ids });
|
|
345
|
+
if (signalService) {
|
|
346
|
+
await signalService.publish({
|
|
347
|
+
recipients: [user],
|
|
348
|
+
message: { action: "mark_unread", notification_ids: ids },
|
|
349
|
+
channel: "notifications"
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (saved === true) {
|
|
354
|
+
await store.markSaved({ user, ids });
|
|
355
|
+
} else if (saved === false) {
|
|
356
|
+
await store.markUnsaved({ user, ids });
|
|
357
|
+
}
|
|
358
|
+
const notifications = await store.getNotifications({ ids, user });
|
|
359
|
+
res.status(200).send(notifications);
|
|
360
|
+
});
|
|
361
|
+
router.post("/", async (req, res) => {
|
|
362
|
+
var _a;
|
|
363
|
+
const { recipients, origin, payload } = req.body;
|
|
364
|
+
const notifications = [];
|
|
365
|
+
let users = [];
|
|
366
|
+
try {
|
|
367
|
+
await authenticateService(req);
|
|
368
|
+
} catch (e) {
|
|
369
|
+
throw new errors.AuthenticationError();
|
|
370
|
+
}
|
|
371
|
+
const { title, link, description, scope } = payload;
|
|
372
|
+
if (!recipients || !title || !origin || !link) {
|
|
373
|
+
logger.error(`Invalid notification request received`);
|
|
374
|
+
throw new errors.InputError();
|
|
375
|
+
}
|
|
376
|
+
let entityRef = null;
|
|
377
|
+
if (recipients.entityRef && recipients.type === "entity") {
|
|
378
|
+
entityRef = recipients.entityRef;
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
users = await getUsersForEntityRef(entityRef);
|
|
382
|
+
} catch (e) {
|
|
383
|
+
throw new errors.InputError();
|
|
384
|
+
}
|
|
385
|
+
const baseNotification = {
|
|
386
|
+
payload: {
|
|
387
|
+
...payload,
|
|
388
|
+
severity: (_a = payload.severity) != null ? _a : "normal"
|
|
389
|
+
},
|
|
390
|
+
origin,
|
|
391
|
+
created: /* @__PURE__ */ new Date()
|
|
392
|
+
};
|
|
393
|
+
const uniqueUsers = [...new Set(users)];
|
|
394
|
+
for (const user of uniqueUsers) {
|
|
395
|
+
const userNotification = {
|
|
396
|
+
...baseNotification,
|
|
397
|
+
id: uuid.v4(),
|
|
398
|
+
user
|
|
399
|
+
};
|
|
400
|
+
const notification = await decorateNotification(userNotification);
|
|
401
|
+
let existingNotification;
|
|
402
|
+
if (scope) {
|
|
403
|
+
existingNotification = await store.getExistingScopeNotification({
|
|
404
|
+
user,
|
|
405
|
+
scope,
|
|
406
|
+
origin
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
let ret = notification;
|
|
410
|
+
if (existingNotification) {
|
|
411
|
+
const restored = await store.restoreExistingNotification({
|
|
412
|
+
id: existingNotification.id,
|
|
413
|
+
notification
|
|
414
|
+
});
|
|
415
|
+
ret = restored != null ? restored : notification;
|
|
416
|
+
} else {
|
|
417
|
+
await store.saveNotification(notification);
|
|
418
|
+
}
|
|
419
|
+
processorSendNotification(ret);
|
|
420
|
+
notifications.push(ret);
|
|
421
|
+
}
|
|
422
|
+
if (signalService) {
|
|
423
|
+
await signalService.publish({
|
|
424
|
+
recipients: entityRef === null ? null : uniqueUsers,
|
|
425
|
+
message: {
|
|
426
|
+
action: "new_notification",
|
|
427
|
+
notification: { title, description, link }
|
|
428
|
+
},
|
|
429
|
+
channel: "notifications"
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
res.json(notifications);
|
|
433
|
+
});
|
|
434
|
+
router.use(backendCommon.errorHandler());
|
|
435
|
+
return router;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
var __accessCheck = (obj, member, msg) => {
|
|
439
|
+
if (!member.has(obj))
|
|
440
|
+
throw TypeError("Cannot " + msg);
|
|
441
|
+
};
|
|
442
|
+
var __privateGet = (obj, member, getter) => {
|
|
443
|
+
__accessCheck(obj, member, "read from private field");
|
|
444
|
+
return getter ? getter.call(obj) : member.get(obj);
|
|
445
|
+
};
|
|
446
|
+
var __privateAdd = (obj, member, value) => {
|
|
447
|
+
if (member.has(obj))
|
|
448
|
+
throw TypeError("Cannot add the same private member more than once");
|
|
449
|
+
member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
450
|
+
};
|
|
451
|
+
var _processors;
|
|
452
|
+
class NotificationsProcessingExtensionPointImpl {
|
|
453
|
+
constructor() {
|
|
454
|
+
__privateAdd(this, _processors, new Array());
|
|
455
|
+
}
|
|
456
|
+
addProcessor(...processors) {
|
|
457
|
+
__privateGet(this, _processors).push(...processors.flat());
|
|
458
|
+
}
|
|
459
|
+
get processors() {
|
|
460
|
+
return __privateGet(this, _processors);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
_processors = new WeakMap();
|
|
464
|
+
const notificationsPlugin = backendPluginApi.createBackendPlugin({
|
|
465
|
+
pluginId: "notifications",
|
|
466
|
+
register(env) {
|
|
467
|
+
const processingExtensions = new NotificationsProcessingExtensionPointImpl();
|
|
468
|
+
env.registerExtensionPoint(
|
|
469
|
+
pluginNotificationsNode.notificationsProcessingExtensionPoint,
|
|
470
|
+
processingExtensions
|
|
471
|
+
);
|
|
472
|
+
env.registerInit({
|
|
473
|
+
deps: {
|
|
474
|
+
httpRouter: backendPluginApi.coreServices.httpRouter,
|
|
475
|
+
logger: backendPluginApi.coreServices.logger,
|
|
476
|
+
identity: backendPluginApi.coreServices.identity,
|
|
477
|
+
database: backendPluginApi.coreServices.database,
|
|
478
|
+
tokenManager: backendPluginApi.coreServices.tokenManager,
|
|
479
|
+
discovery: backendPluginApi.coreServices.discovery,
|
|
480
|
+
signals: pluginSignalsNode.signalService
|
|
481
|
+
},
|
|
482
|
+
async init({
|
|
483
|
+
httpRouter,
|
|
484
|
+
logger,
|
|
485
|
+
identity,
|
|
486
|
+
database,
|
|
487
|
+
tokenManager,
|
|
488
|
+
discovery,
|
|
489
|
+
signals
|
|
490
|
+
}) {
|
|
491
|
+
httpRouter.use(
|
|
492
|
+
await createRouter({
|
|
493
|
+
logger,
|
|
494
|
+
identity,
|
|
495
|
+
database,
|
|
496
|
+
tokenManager,
|
|
497
|
+
discovery,
|
|
498
|
+
signalService: signals,
|
|
499
|
+
processors: processingExtensions.processors
|
|
500
|
+
})
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
exports["default"] = notificationsPlugin;
|
|
508
|
+
//# sourceMappingURL=index.cjs.js.map
|
|
@@ -0,0 +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: row.created,\n done: row.done,\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 getNotificationsBaseQuery = (\n options: NotificationGetOptions | NotificationModifyOptions,\n ) => {\n const { user, type } = options;\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 (type === 'undone') {\n query.whereNull('done');\n } else if (type === 'done') {\n query.whereNotNull('done');\n } else if (type === 'saved') {\n query.whereNotNull('saved');\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 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 saveNotification(notification: Notification) {\n await this.db.insert(notification).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: options.notification.created,\n severity: options.notification.payload.severity,\n read: null,\n done: 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 markDone(options: NotificationModifyOptions): Promise<void> {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n await notificationQuery.update({ done: new Date(), read: new Date() });\n }\n\n async markUndone(options: NotificationModifyOptions): Promise<void> {\n const notificationQuery = this.getNotificationsBaseQuery(options);\n await notificationQuery.update({ done: null, 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 {\n errorHandler,\n PluginDatabaseManager,\n TokenManager,\n} from '@backstage/backend-common';\nimport express, { Request } from 'express';\nimport Router from 'express-promise-router';\nimport {\n getBearerTokenFromAuthorizationHeader,\n IdentityApi,\n} from '@backstage/plugin-auth-node';\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 { AuthenticationError, InputError } from '@backstage/errors';\nimport { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api';\nimport { SignalService } from '@backstage/plugin-signals-node';\nimport {\n Notification,\n NotificationType,\n} from '@backstage/plugin-notifications-common';\n\n/** @internal */\nexport interface RouterOptions {\n logger: LoggerService;\n identity: IdentityApi;\n database: PluginDatabaseManager;\n tokenManager: TokenManager;\n discovery: DiscoveryService;\n signalService?: SignalService;\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 identity,\n discovery,\n catalog,\n tokenManager,\n processors,\n signalService,\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 user = await identity.getIdentity({ request: req });\n if (!user) {\n throw new AuthenticationError();\n }\n return user.identity.userEntityRef;\n };\n\n const authenticateService = async (req: Request<unknown>) => {\n const token = getBearerTokenFromAuthorizationHeader(\n req.header('authorization'),\n );\n if (!token) {\n throw new AuthenticationError();\n }\n await tokenManager.authenticate(token);\n };\n\n const getUsersForEntityRef = async (\n entityRef: string | string[] | null,\n ): Promise<string[]> => {\n const { token } = await tokenManager.getToken();\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.type) {\n opts.type = req.query.type.toString() as NotificationType;\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.search) {\n opts.search = req.query.search.toString();\n }\n\n const notifications = await store.getNotifications(opts);\n res.send(notifications);\n });\n\n router.get('/status', async (req, res) => {\n const user = await getUser(req);\n const status = await store.getStatus({ user, type: 'undone' });\n res.send(status);\n });\n\n router.post('/update', async (req, res) => {\n const user = await getUser(req);\n const { ids, done, read, saved } = req.body;\n if (!ids || !Array.isArray(ids)) {\n throw new InputError();\n }\n\n if (done === true) {\n await store.markDone({ user, ids });\n if (signalService) {\n await signalService.publish({\n recipients: [user],\n message: { action: 'done', notification_ids: ids },\n channel: 'notifications',\n });\n }\n } else if (done === false) {\n await store.markUndone({ user, ids });\n if (signalService) {\n await signalService.publish({\n recipients: [user],\n message: { action: 'undone', notification_ids: ids },\n channel: 'notifications',\n });\n }\n }\n\n if (read === true) {\n await store.markRead({ user, ids });\n\n if (signalService) {\n await signalService.publish({\n recipients: [user],\n message: { action: 'mark_read', notification_ids: ids },\n channel: 'notifications',\n });\n }\n } else if (read === false) {\n await store.markUnread({ user: user, ids });\n\n if (signalService) {\n await signalService.publish({\n recipients: [user],\n message: { action: 'mark_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 // Allowed only for service-to-service authentication, uses `getUsersForEntityRef` to retrieve recipients for\n // specific entity reference\n router.post('/', async (req, res) => {\n const { recipients, origin, payload } = req.body;\n const notifications = [];\n let users = [];\n\n try {\n await authenticateService(req);\n } catch (e) {\n throw new AuthenticationError();\n }\n\n const { title, link, description, scope } = payload;\n\n if (!recipients || !title || !origin || !link) {\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 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\n if (signalService) {\n await signalService.publish({\n recipients: entityRef === null ? null : uniqueUsers,\n message: {\n action: 'new_notification',\n notification: { title, description, link },\n },\n channel: 'notifications',\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 */\nimport {\n coreServices,\n createBackendPlugin,\n} from '@backstage/backend-plugin-api';\nimport { createRouter } from './service/router';\nimport { signalService } 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 httpRouter: coreServices.httpRouter,\n logger: coreServices.logger,\n identity: coreServices.identity,\n database: coreServices.database,\n tokenManager: coreServices.tokenManager,\n discovery: coreServices.discovery,\n signals: signalService,\n },\n async init({\n httpRouter,\n logger,\n identity,\n database,\n tokenManager,\n discovery,\n signals,\n }) {\n httpRouter.use(\n await createRouter({\n logger,\n identity,\n database,\n tokenManager,\n discovery,\n signalService: signals,\n processors: processingExtensions.processors,\n }),\n );\n },\n });\n },\n});\n"],"names":["resolvePackagePath","catalogClient","CatalogClient","AuthenticationError","getBearerTokenFromAuthorizationHeader","isUserEntity","stringifyEntityRef","isGroupEntity","users","RELATION_HAS_MEMBER","Router","express","InputError","uuid","errorHandler","createBackendPlugin","notificationsProcessingExtensionPoint","coreServices","signalService"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as _backstage_backend_plugin_api from '@backstage/backend-plugin-api';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Notifications backend plugin
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
declare const notificationsPlugin: () => _backstage_backend_plugin_api.BackendFeature;
|
|
9
|
+
|
|
10
|
+
export { notificationsPlugin as default };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@backstage/plugin-notifications-backend",
|
|
3
|
+
"version": "0.0.1-next.0",
|
|
4
|
+
"main": "dist/index.cjs.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"main": "dist/index.cjs.js",
|
|
10
|
+
"types": "dist/index.d.ts"
|
|
11
|
+
},
|
|
12
|
+
"backstage": {
|
|
13
|
+
"role": "backend-plugin"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"start": "backstage-cli package start",
|
|
17
|
+
"build": "backstage-cli package build",
|
|
18
|
+
"lint": "backstage-cli package lint",
|
|
19
|
+
"test": "backstage-cli package test",
|
|
20
|
+
"clean": "backstage-cli package clean",
|
|
21
|
+
"prepack": "backstage-cli package prepack",
|
|
22
|
+
"postpack": "backstage-cli package postpack"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@backstage/backend-common": "^0.21.0-next.2",
|
|
26
|
+
"@backstage/backend-plugin-api": "^0.6.10-next.2",
|
|
27
|
+
"@backstage/catalog-client": "^1.6.0-next.1",
|
|
28
|
+
"@backstage/catalog-model": "^1.4.4-next.0",
|
|
29
|
+
"@backstage/config": "^1.1.1",
|
|
30
|
+
"@backstage/errors": "^1.2.3",
|
|
31
|
+
"@backstage/plugin-auth-node": "^0.4.4-next.2",
|
|
32
|
+
"@backstage/plugin-events-node": "^0.2.19-next.2",
|
|
33
|
+
"@backstage/plugin-notifications-common": "^0.0.1-next.0",
|
|
34
|
+
"@backstage/plugin-notifications-node": "^0.0.1-next.0",
|
|
35
|
+
"@backstage/plugin-signals-node": "^0.0.1-next.2",
|
|
36
|
+
"express": "^4.17.1",
|
|
37
|
+
"express-promise-router": "^4.1.0",
|
|
38
|
+
"knex": "^3.0.0",
|
|
39
|
+
"node-fetch": "^2.6.7",
|
|
40
|
+
"uuid": "^8.0.0",
|
|
41
|
+
"winston": "^3.2.1",
|
|
42
|
+
"yn": "^4.0.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@backstage/backend-test-utils": "^0.3.0-next.2",
|
|
46
|
+
"@backstage/cli": "^0.25.2-next.2",
|
|
47
|
+
"@types/express": "^4.17.6",
|
|
48
|
+
"@types/supertest": "^2.0.8",
|
|
49
|
+
"msw": "^1.0.0",
|
|
50
|
+
"supertest": "^6.2.4"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist"
|
|
54
|
+
]
|
|
55
|
+
}
|