@backstage/plugin-events-backend 0.3.13-next.0 → 0.3.13-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/alpha/package.json +1 -1
- package/dist/alpha.cjs.js +2 -985
- package/dist/alpha.cjs.js.map +1 -1
- package/dist/index.cjs.js +5 -61
- package/dist/index.cjs.js.map +1 -1
- package/dist/schema/openapi.generated.cjs.js +272 -0
- package/dist/schema/openapi.generated.cjs.js.map +1 -0
- package/dist/service/DefaultEventBroker.cjs.js +32 -0
- package/dist/service/DefaultEventBroker.cjs.js.map +1 -0
- package/dist/service/EventsBackend.cjs.js +36 -0
- package/dist/service/EventsBackend.cjs.js.map +1 -0
- package/dist/service/EventsPlugin.cjs.js +98 -0
- package/dist/service/EventsPlugin.cjs.js.map +1 -0
- package/dist/{cjs/HttpPostIngressEventPublisher-D6pQ6awS.cjs.js → service/http/HttpPostIngressEventPublisher.cjs.js} +3 -18
- package/dist/service/http/HttpPostIngressEventPublisher.cjs.js.map +1 -0
- package/dist/service/http/validation/RequestValidationContextImpl.cjs.js +20 -0
- package/dist/service/http/validation/RequestValidationContextImpl.cjs.js.map +1 -0
- package/dist/service/hub/DatabaseEventBusStore.cjs.js +410 -0
- package/dist/service/hub/DatabaseEventBusStore.cjs.js.map +1 -0
- package/dist/service/hub/MemoryEventBusStore.cjs.js +106 -0
- package/dist/service/hub/MemoryEventBusStore.cjs.js.map +1 -0
- package/dist/service/hub/createEventBusRouter.cjs.js +130 -0
- package/dist/service/hub/createEventBusRouter.cjs.js.map +1 -0
- package/package.json +13 -13
- package/dist/cjs/HttpPostIngressEventPublisher-D6pQ6awS.cjs.js.map +0 -1
package/dist/alpha.cjs.js
CHANGED
|
@@ -2,992 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var
|
|
6
|
-
var alpha = require('@backstage/plugin-events-node/alpha');
|
|
7
|
-
var pluginEventsNode = require('@backstage/plugin-events-node');
|
|
8
|
-
var Router = require('express-promise-router');
|
|
9
|
-
var HttpPostIngressEventPublisher = require('./cjs/HttpPostIngressEventPublisher-D6pQ6awS.cjs.js');
|
|
10
|
-
var backendOpenapiUtils = require('@backstage/backend-openapi-utils');
|
|
11
|
-
var errors = require('@backstage/errors');
|
|
12
|
-
var types = require('@backstage/types');
|
|
13
|
-
require('@backstage/backend-common');
|
|
14
|
-
require('express');
|
|
5
|
+
var EventsPlugin = require('./service/EventsPlugin.cjs.js');
|
|
15
6
|
|
|
16
|
-
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
|
|
17
7
|
|
|
18
|
-
var Router__default = /*#__PURE__*/_interopDefaultCompat(Router);
|
|
19
8
|
|
|
20
|
-
|
|
21
|
-
openapi: "3.0.3",
|
|
22
|
-
info: {
|
|
23
|
-
title: "events",
|
|
24
|
-
version: "1",
|
|
25
|
-
description: "The Backstage backend plugin that powers the events system.",
|
|
26
|
-
license: {
|
|
27
|
-
name: "Apache-2.0",
|
|
28
|
-
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
|
|
29
|
-
},
|
|
30
|
-
contact: {}
|
|
31
|
-
},
|
|
32
|
-
servers: [
|
|
33
|
-
{
|
|
34
|
-
url: "/"
|
|
35
|
-
}
|
|
36
|
-
],
|
|
37
|
-
components: {
|
|
38
|
-
examples: {},
|
|
39
|
-
headers: {},
|
|
40
|
-
parameters: {
|
|
41
|
-
subscriptionId: {
|
|
42
|
-
name: "subscriptionId",
|
|
43
|
-
in: "path",
|
|
44
|
-
required: true,
|
|
45
|
-
allowReserved: true,
|
|
46
|
-
schema: {
|
|
47
|
-
type: "string"
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
requestBodies: {},
|
|
52
|
-
responses: {
|
|
53
|
-
ErrorResponse: {
|
|
54
|
-
description: "An error response from the backend.",
|
|
55
|
-
content: {
|
|
56
|
-
"application/json; charset=utf-8": {
|
|
57
|
-
schema: {
|
|
58
|
-
$ref: "#/components/schemas/Error"
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
schemas: {
|
|
65
|
-
Event: {
|
|
66
|
-
type: "object",
|
|
67
|
-
required: ["topic", "payload"],
|
|
68
|
-
properties: {
|
|
69
|
-
topic: {
|
|
70
|
-
type: "string",
|
|
71
|
-
description: "The topic that the event is published on"
|
|
72
|
-
},
|
|
73
|
-
payload: {
|
|
74
|
-
description: "The event payload"
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
Error: {
|
|
79
|
-
type: "object",
|
|
80
|
-
properties: {
|
|
81
|
-
error: {
|
|
82
|
-
type: "object",
|
|
83
|
-
properties: {
|
|
84
|
-
name: {
|
|
85
|
-
type: "string"
|
|
86
|
-
},
|
|
87
|
-
message: {
|
|
88
|
-
type: "string"
|
|
89
|
-
}
|
|
90
|
-
},
|
|
91
|
-
required: ["name", "message"]
|
|
92
|
-
},
|
|
93
|
-
request: {
|
|
94
|
-
type: "object",
|
|
95
|
-
properties: {
|
|
96
|
-
method: {
|
|
97
|
-
type: "string"
|
|
98
|
-
},
|
|
99
|
-
url: {
|
|
100
|
-
type: "string"
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
required: ["method", "url"]
|
|
104
|
-
},
|
|
105
|
-
response: {
|
|
106
|
-
type: "object",
|
|
107
|
-
properties: {
|
|
108
|
-
statusCode: {
|
|
109
|
-
type: "number"
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
required: ["statusCode"]
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
required: ["error", "request", "response"]
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
securitySchemes: {
|
|
119
|
-
JWT: {
|
|
120
|
-
type: "http",
|
|
121
|
-
scheme: "bearer",
|
|
122
|
-
bearerFormat: "JWT"
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
paths: {
|
|
127
|
-
"/bus/v1/events": {
|
|
128
|
-
post: {
|
|
129
|
-
operationId: "PostEvent",
|
|
130
|
-
description: "Publish a new event",
|
|
131
|
-
responses: {
|
|
132
|
-
"201": {
|
|
133
|
-
description: "The event was published successfully"
|
|
134
|
-
},
|
|
135
|
-
"204": {
|
|
136
|
-
description: "The event did not need to be published as all subscribers have already been notified"
|
|
137
|
-
},
|
|
138
|
-
default: {
|
|
139
|
-
$ref: "#/components/responses/ErrorResponse"
|
|
140
|
-
}
|
|
141
|
-
},
|
|
142
|
-
security: [
|
|
143
|
-
{},
|
|
144
|
-
{
|
|
145
|
-
JWT: []
|
|
146
|
-
}
|
|
147
|
-
],
|
|
148
|
-
requestBody: {
|
|
149
|
-
required: true,
|
|
150
|
-
content: {
|
|
151
|
-
"application/json": {
|
|
152
|
-
schema: {
|
|
153
|
-
type: "object",
|
|
154
|
-
required: ["event"],
|
|
155
|
-
properties: {
|
|
156
|
-
event: {
|
|
157
|
-
$ref: "#/components/schemas/Event"
|
|
158
|
-
},
|
|
159
|
-
notifiedSubscribers: {
|
|
160
|
-
type: "array",
|
|
161
|
-
description: "The IDs of subscriptions that have already received this event",
|
|
162
|
-
items: {
|
|
163
|
-
type: "string"
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
examples: {
|
|
169
|
-
"Publishing a simple Event": {
|
|
170
|
-
value: {
|
|
171
|
-
event: {
|
|
172
|
-
topic: "test-topic",
|
|
173
|
-
payload: {
|
|
174
|
-
myData: "foo"
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
},
|
|
185
|
-
"/bus/v1/subscriptions/{subscriptionId}": {
|
|
186
|
-
put: {
|
|
187
|
-
operationId: "PutSubscription",
|
|
188
|
-
description: "Ensures that the subscription exists with the provided configuration",
|
|
189
|
-
responses: {
|
|
190
|
-
"201": {
|
|
191
|
-
description: "The subscription exists or was created successfully"
|
|
192
|
-
},
|
|
193
|
-
default: {
|
|
194
|
-
$ref: "#/components/responses/ErrorResponse"
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
security: [
|
|
198
|
-
{},
|
|
199
|
-
{
|
|
200
|
-
JWT: []
|
|
201
|
-
}
|
|
202
|
-
],
|
|
203
|
-
parameters: [
|
|
204
|
-
{
|
|
205
|
-
$ref: "#/components/parameters/subscriptionId"
|
|
206
|
-
}
|
|
207
|
-
],
|
|
208
|
-
requestBody: {
|
|
209
|
-
required: true,
|
|
210
|
-
content: {
|
|
211
|
-
"application/json": {
|
|
212
|
-
schema: {
|
|
213
|
-
type: "object",
|
|
214
|
-
required: ["topics"],
|
|
215
|
-
properties: {
|
|
216
|
-
topics: {
|
|
217
|
-
type: "array",
|
|
218
|
-
description: "The topics to subscribe to",
|
|
219
|
-
items: {
|
|
220
|
-
type: "string"
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
examples: {
|
|
226
|
-
"Subscribing to a single topic": {
|
|
227
|
-
value: {
|
|
228
|
-
topics: ["test-topic"]
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
},
|
|
237
|
-
"/bus/v1/subscriptions/{subscriptionId}/events": {
|
|
238
|
-
get: {
|
|
239
|
-
operationId: "GetSubscriptionEvents",
|
|
240
|
-
description: "Get new events for the provided subscription",
|
|
241
|
-
responses: {
|
|
242
|
-
"200": {
|
|
243
|
-
description: "New events",
|
|
244
|
-
content: {
|
|
245
|
-
"application/json": {
|
|
246
|
-
schema: {
|
|
247
|
-
type: "object",
|
|
248
|
-
required: ["events"],
|
|
249
|
-
properties: {
|
|
250
|
-
events: {
|
|
251
|
-
type: "array",
|
|
252
|
-
items: {
|
|
253
|
-
$ref: "#/components/schemas/Event"
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
"202": {
|
|
262
|
-
description: "No new events are available. Response will block until the client should try again."
|
|
263
|
-
},
|
|
264
|
-
default: {
|
|
265
|
-
$ref: "#/components/responses/ErrorResponse"
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
security: [
|
|
269
|
-
{},
|
|
270
|
-
{
|
|
271
|
-
JWT: []
|
|
272
|
-
}
|
|
273
|
-
],
|
|
274
|
-
parameters: [
|
|
275
|
-
{
|
|
276
|
-
$ref: "#/components/parameters/subscriptionId"
|
|
277
|
-
}
|
|
278
|
-
]
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
const createOpenApiRouter = async (options) => backendOpenapiUtils.createValidatedOpenApiRouter(spec, options);
|
|
284
|
-
|
|
285
|
-
const MAX_BATCH_SIZE$1 = 10;
|
|
286
|
-
const MAX_EVENTS_DEFAULT = 1e3;
|
|
287
|
-
class MemoryEventBusStore {
|
|
288
|
-
#maxEvents;
|
|
289
|
-
#events = new Array();
|
|
290
|
-
#subscribers = /* @__PURE__ */ new Map();
|
|
291
|
-
#listeners = /* @__PURE__ */ new Set();
|
|
292
|
-
constructor(options = {}) {
|
|
293
|
-
this.#maxEvents = options.maxEvents ?? MAX_EVENTS_DEFAULT;
|
|
294
|
-
}
|
|
295
|
-
async publish(options) {
|
|
296
|
-
const topic = options.event.topic;
|
|
297
|
-
const notifiedSubscribers = new Set(options.notifiedSubscribers);
|
|
298
|
-
let hasOtherSubscribers = false;
|
|
299
|
-
for (const sub of this.#subscribers.values()) {
|
|
300
|
-
if (sub.topics.has(topic) && !notifiedSubscribers.has(sub.id)) {
|
|
301
|
-
hasOtherSubscribers = true;
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
if (!hasOtherSubscribers) {
|
|
306
|
-
return void 0;
|
|
307
|
-
}
|
|
308
|
-
const nextSeq = this.#getMaxSeq() + 1;
|
|
309
|
-
this.#events.push({ ...options.event, notifiedSubscribers, seq: nextSeq });
|
|
310
|
-
for (const listener of this.#listeners) {
|
|
311
|
-
if (listener.topics.has(topic)) {
|
|
312
|
-
listener.resolve({ topic });
|
|
313
|
-
this.#listeners.delete(listener);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
if (this.#events.length > this.#maxEvents) {
|
|
317
|
-
this.#events.shift();
|
|
318
|
-
}
|
|
319
|
-
return { eventId: String(nextSeq) };
|
|
320
|
-
}
|
|
321
|
-
#getMaxSeq() {
|
|
322
|
-
return this.#events[this.#events.length - 1]?.seq ?? 0;
|
|
323
|
-
}
|
|
324
|
-
async upsertSubscription(id, topics) {
|
|
325
|
-
const existing = this.#subscribers.get(id);
|
|
326
|
-
if (existing) {
|
|
327
|
-
existing.topics = new Set(topics);
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
const sub = {
|
|
331
|
-
id,
|
|
332
|
-
seq: this.#getMaxSeq(),
|
|
333
|
-
topics: new Set(topics)
|
|
334
|
-
};
|
|
335
|
-
this.#subscribers.set(id, sub);
|
|
336
|
-
}
|
|
337
|
-
async readSubscription(id) {
|
|
338
|
-
const sub = this.#subscribers.get(id);
|
|
339
|
-
if (!sub) {
|
|
340
|
-
throw new errors.NotFoundError(`Subscription not found`);
|
|
341
|
-
}
|
|
342
|
-
const events = this.#events.filter(
|
|
343
|
-
(event) => event.seq > sub.seq && sub.topics.has(event.topic) && !event.notifiedSubscribers.has(id)
|
|
344
|
-
).slice(0, MAX_BATCH_SIZE$1);
|
|
345
|
-
sub.seq = events[events.length - 1]?.seq ?? sub.seq;
|
|
346
|
-
return {
|
|
347
|
-
events: events.map(({ topic, eventPayload }) => ({
|
|
348
|
-
topic,
|
|
349
|
-
eventPayload
|
|
350
|
-
}))
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
async setupListener(subscriptionId, options) {
|
|
354
|
-
return {
|
|
355
|
-
waitForUpdate: async () => {
|
|
356
|
-
options.signal.throwIfAborted();
|
|
357
|
-
const sub = this.#subscribers.get(subscriptionId);
|
|
358
|
-
if (!sub) {
|
|
359
|
-
throw new errors.NotFoundError(`Subscription not found`);
|
|
360
|
-
}
|
|
361
|
-
return new Promise((resolve, reject) => {
|
|
362
|
-
const listener = {
|
|
363
|
-
topics: sub.topics,
|
|
364
|
-
resolve(result) {
|
|
365
|
-
resolve(result);
|
|
366
|
-
cleanup();
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
this.#listeners.add(listener);
|
|
370
|
-
const onAbort = () => {
|
|
371
|
-
this.#listeners.delete(listener);
|
|
372
|
-
reject(options.signal.reason);
|
|
373
|
-
cleanup();
|
|
374
|
-
};
|
|
375
|
-
function cleanup() {
|
|
376
|
-
options.signal.removeEventListener("abort", onAbort);
|
|
377
|
-
}
|
|
378
|
-
options.signal.addEventListener("abort", onAbort);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
const WINDOW_MAX_COUNT_DEFAULT = 1e4;
|
|
386
|
-
const WINDOW_MIN_AGE_DEFAULT = { minutes: 10 };
|
|
387
|
-
const WINDOW_MAX_AGE_DEFAULT = { days: 1 };
|
|
388
|
-
const MAX_BATCH_SIZE = 10;
|
|
389
|
-
const LISTENER_CONNECTION_TIMEOUT_MS = 6e4;
|
|
390
|
-
const KEEPALIVE_INTERVAL_MS = 6e4;
|
|
391
|
-
const TABLE_EVENTS = "event_bus_events";
|
|
392
|
-
const TABLE_SUBSCRIPTIONS = "event_bus_subscriptions";
|
|
393
|
-
const TOPIC_PUBLISH = "event_bus_publish";
|
|
394
|
-
function creatorId(credentials) {
|
|
395
|
-
return `service=${credentials.principal.subject}`;
|
|
396
|
-
}
|
|
397
|
-
const migrationsDir = backendPluginApi.resolvePackagePath(
|
|
398
|
-
"@backstage/plugin-events-backend",
|
|
399
|
-
"migrations"
|
|
400
|
-
);
|
|
401
|
-
class DatabaseEventBusListener {
|
|
402
|
-
#client;
|
|
403
|
-
#logger;
|
|
404
|
-
#listeners = /* @__PURE__ */ new Set();
|
|
405
|
-
#isShuttingDown = false;
|
|
406
|
-
#connPromise;
|
|
407
|
-
#connTimeout;
|
|
408
|
-
#keepaliveInterval;
|
|
409
|
-
constructor(client, logger) {
|
|
410
|
-
this.#client = client;
|
|
411
|
-
this.#logger = logger.child({ type: "DatabaseEventBusListener" });
|
|
412
|
-
}
|
|
413
|
-
async setupListener(topics, signal) {
|
|
414
|
-
if (this.#connTimeout) {
|
|
415
|
-
clearTimeout(this.#connTimeout);
|
|
416
|
-
this.#connTimeout = void 0;
|
|
417
|
-
}
|
|
418
|
-
await this.#ensureConnection();
|
|
419
|
-
const updatePromise = new Promise((resolve, reject) => {
|
|
420
|
-
const listener = {
|
|
421
|
-
topics,
|
|
422
|
-
resolve(result) {
|
|
423
|
-
resolve(result);
|
|
424
|
-
cleanup();
|
|
425
|
-
},
|
|
426
|
-
reject(err) {
|
|
427
|
-
reject(err);
|
|
428
|
-
cleanup();
|
|
429
|
-
}
|
|
430
|
-
};
|
|
431
|
-
this.#listeners.add(listener);
|
|
432
|
-
const onAbort = () => {
|
|
433
|
-
this.#listeners.delete(listener);
|
|
434
|
-
this.#maybeTimeoutConnection();
|
|
435
|
-
reject(signal.reason);
|
|
436
|
-
cleanup();
|
|
437
|
-
};
|
|
438
|
-
function cleanup() {
|
|
439
|
-
signal.removeEventListener("abort", onAbort);
|
|
440
|
-
}
|
|
441
|
-
signal.addEventListener("abort", onAbort);
|
|
442
|
-
});
|
|
443
|
-
updatePromise.catch(() => {
|
|
444
|
-
});
|
|
445
|
-
return { waitForUpdate: () => updatePromise };
|
|
446
|
-
}
|
|
447
|
-
async shutdown() {
|
|
448
|
-
if (this.#isShuttingDown) {
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
this.#isShuttingDown = true;
|
|
452
|
-
const conn = await this.#connPromise?.catch(() => void 0);
|
|
453
|
-
if (conn) {
|
|
454
|
-
this.#destroyConnection(conn);
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
#handleNotify(topic) {
|
|
458
|
-
this.#logger.debug(`Listener received notification for topic '${topic}'`);
|
|
459
|
-
for (const l of this.#listeners) {
|
|
460
|
-
if (l.topics.has(topic)) {
|
|
461
|
-
l.resolve({ topic });
|
|
462
|
-
this.#listeners.delete(l);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
this.#maybeTimeoutConnection();
|
|
466
|
-
}
|
|
467
|
-
// We don't try to reconnect on error, instead we notify all listeners and let
|
|
468
|
-
// them try to establish a new connection
|
|
469
|
-
#handleError(error) {
|
|
470
|
-
this.#logger.error(
|
|
471
|
-
`Listener connection failed, notifying all listeners`,
|
|
472
|
-
error
|
|
473
|
-
);
|
|
474
|
-
for (const l of this.#listeners) {
|
|
475
|
-
l.reject(new Error("Listener connection failed"));
|
|
476
|
-
}
|
|
477
|
-
this.#listeners.clear();
|
|
478
|
-
this.#maybeTimeoutConnection();
|
|
479
|
-
}
|
|
480
|
-
#maybeTimeoutConnection() {
|
|
481
|
-
if (this.#listeners.size === 0 && !this.#connTimeout) {
|
|
482
|
-
this.#connTimeout = setTimeout(() => {
|
|
483
|
-
this.#connTimeout = void 0;
|
|
484
|
-
this.#connPromise?.then((conn) => {
|
|
485
|
-
this.#logger.info("Listener connection timed out, destroying");
|
|
486
|
-
this.#connPromise = void 0;
|
|
487
|
-
this.#destroyConnection(conn);
|
|
488
|
-
});
|
|
489
|
-
}, LISTENER_CONNECTION_TIMEOUT_MS);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
#destroyConnection(conn) {
|
|
493
|
-
if (this.#keepaliveInterval) {
|
|
494
|
-
clearInterval(this.#keepaliveInterval);
|
|
495
|
-
this.#keepaliveInterval = void 0;
|
|
496
|
-
}
|
|
497
|
-
this.#client.destroyRawConnection(conn).catch((error) => {
|
|
498
|
-
this.#logger.error(`Listener failed to destroy connection`, error);
|
|
499
|
-
});
|
|
500
|
-
conn.removeAllListeners();
|
|
501
|
-
}
|
|
502
|
-
async #ensureConnection() {
|
|
503
|
-
if (this.#isShuttingDown) {
|
|
504
|
-
throw new Error("Listener is shutting down");
|
|
505
|
-
}
|
|
506
|
-
if (this.#connPromise) {
|
|
507
|
-
await this.#connPromise;
|
|
508
|
-
return;
|
|
509
|
-
}
|
|
510
|
-
this.#connPromise = Promise.resolve().then(async () => {
|
|
511
|
-
const conn = await this.#client.acquireRawConnection();
|
|
512
|
-
try {
|
|
513
|
-
await conn.query(`LISTEN ${TOPIC_PUBLISH}`);
|
|
514
|
-
if (this.#keepaliveInterval) {
|
|
515
|
-
clearInterval(this.#keepaliveInterval);
|
|
516
|
-
}
|
|
517
|
-
this.#keepaliveInterval = setInterval(() => {
|
|
518
|
-
conn.query("select 1").catch((error) => {
|
|
519
|
-
this.#connPromise = void 0;
|
|
520
|
-
this.#destroyConnection(conn);
|
|
521
|
-
this.#handleError(new errors.ForwardedError("Keepalive failed", error));
|
|
522
|
-
});
|
|
523
|
-
}, KEEPALIVE_INTERVAL_MS);
|
|
524
|
-
conn.on("notification", (event) => {
|
|
525
|
-
this.#handleNotify(event.payload);
|
|
526
|
-
});
|
|
527
|
-
conn.on("error", (error) => {
|
|
528
|
-
this.#connPromise = void 0;
|
|
529
|
-
this.#destroyConnection(conn);
|
|
530
|
-
this.#handleError(error);
|
|
531
|
-
});
|
|
532
|
-
conn.on("end", (error) => {
|
|
533
|
-
this.#connPromise = void 0;
|
|
534
|
-
this.#destroyConnection(conn);
|
|
535
|
-
this.#handleError(
|
|
536
|
-
error ?? new Error("Connection ended unexpectedly")
|
|
537
|
-
);
|
|
538
|
-
});
|
|
539
|
-
return conn;
|
|
540
|
-
} catch (error) {
|
|
541
|
-
this.#destroyConnection(conn);
|
|
542
|
-
throw error;
|
|
543
|
-
}
|
|
544
|
-
});
|
|
545
|
-
try {
|
|
546
|
-
await this.#connPromise;
|
|
547
|
-
} catch (error) {
|
|
548
|
-
this.#connPromise = void 0;
|
|
549
|
-
throw error;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
class DatabaseEventBusStore {
|
|
554
|
-
static async create(options) {
|
|
555
|
-
const db = await options.database.getClient();
|
|
556
|
-
if (db.client.config.client !== "pg") {
|
|
557
|
-
throw new Error(
|
|
558
|
-
`DatabaseEventBusStore only supports PostgreSQL, got '${db.client.config.client}'`
|
|
559
|
-
);
|
|
560
|
-
}
|
|
561
|
-
if (!options.database.migrations?.skip) {
|
|
562
|
-
await db.migrate.latest({
|
|
563
|
-
directory: migrationsDir
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
const listener = new DatabaseEventBusListener(db.client, options.logger);
|
|
567
|
-
const store = new DatabaseEventBusStore(
|
|
568
|
-
db,
|
|
569
|
-
options.logger,
|
|
570
|
-
listener,
|
|
571
|
-
options.window?.maxCount ?? WINDOW_MAX_COUNT_DEFAULT,
|
|
572
|
-
types.durationToMilliseconds(options.window?.minAge ?? WINDOW_MIN_AGE_DEFAULT),
|
|
573
|
-
types.durationToMilliseconds(options.window?.maxAge ?? WINDOW_MAX_AGE_DEFAULT)
|
|
574
|
-
);
|
|
575
|
-
await options.scheduler.scheduleTask({
|
|
576
|
-
id: "event-bus-cleanup",
|
|
577
|
-
frequency: { seconds: 10 },
|
|
578
|
-
timeout: { minutes: 1 },
|
|
579
|
-
initialDelay: { seconds: 10 },
|
|
580
|
-
fn: () => store.#cleanup()
|
|
581
|
-
});
|
|
582
|
-
options.lifecycle.addShutdownHook(async () => {
|
|
583
|
-
await listener.shutdown();
|
|
584
|
-
});
|
|
585
|
-
return store;
|
|
586
|
-
}
|
|
587
|
-
/** @internal */
|
|
588
|
-
static async forTest({
|
|
589
|
-
db,
|
|
590
|
-
logger,
|
|
591
|
-
minAge = 0,
|
|
592
|
-
maxAge = 1e4
|
|
593
|
-
}) {
|
|
594
|
-
await db.migrate.latest({ directory: migrationsDir });
|
|
595
|
-
const store = new DatabaseEventBusStore(
|
|
596
|
-
db,
|
|
597
|
-
logger,
|
|
598
|
-
new DatabaseEventBusListener(db.client, logger),
|
|
599
|
-
5,
|
|
600
|
-
minAge,
|
|
601
|
-
maxAge
|
|
602
|
-
);
|
|
603
|
-
return Object.assign(store, { clean: () => store.#cleanup() });
|
|
604
|
-
}
|
|
605
|
-
#db;
|
|
606
|
-
#logger;
|
|
607
|
-
#listener;
|
|
608
|
-
#windowMaxCount;
|
|
609
|
-
#windowMinAge;
|
|
610
|
-
#windowMaxAge;
|
|
611
|
-
constructor(db, logger, listener, windowMaxCount, windowMinAge, windowMaxAge) {
|
|
612
|
-
this.#db = db;
|
|
613
|
-
this.#logger = logger;
|
|
614
|
-
this.#listener = listener;
|
|
615
|
-
this.#windowMaxCount = windowMaxCount;
|
|
616
|
-
this.#windowMinAge = windowMinAge;
|
|
617
|
-
this.#windowMaxAge = windowMaxAge;
|
|
618
|
-
}
|
|
619
|
-
async publish(options) {
|
|
620
|
-
const topic = options.event.topic;
|
|
621
|
-
const notifiedSubscribers = options.notifiedSubscribers ?? [];
|
|
622
|
-
const result = await this.#db.into(
|
|
623
|
-
this.#db.raw("?? (??, ??, ??, ??)", [
|
|
624
|
-
TABLE_EVENTS,
|
|
625
|
-
// These are the rows that we insert, and should match the SELECT below
|
|
626
|
-
"created_by",
|
|
627
|
-
"topic",
|
|
628
|
-
"data_json",
|
|
629
|
-
"notified_subscribers"
|
|
630
|
-
])
|
|
631
|
-
).insert(
|
|
632
|
-
(q) => q.select(
|
|
633
|
-
this.#db.raw("?", [creatorId(options.credentials)]),
|
|
634
|
-
this.#db.raw("?", [topic]),
|
|
635
|
-
this.#db.raw("?", [
|
|
636
|
-
JSON.stringify({
|
|
637
|
-
payload: options.event.eventPayload,
|
|
638
|
-
metadata: options.event.metadata
|
|
639
|
-
})
|
|
640
|
-
]),
|
|
641
|
-
this.#db.raw("?", [notifiedSubscribers])
|
|
642
|
-
).from(TABLE_SUBSCRIPTIONS).whereNotIn("id", notifiedSubscribers).andWhere(this.#db.raw("? = ANY(topics)", [topic])).having(this.#db.raw("count(*)"), ">", 0)
|
|
643
|
-
// Check if there are any results
|
|
644
|
-
).returning("id");
|
|
645
|
-
if (result.length === 0) {
|
|
646
|
-
return void 0;
|
|
647
|
-
}
|
|
648
|
-
if (result.length > 1) {
|
|
649
|
-
throw new Error(
|
|
650
|
-
`Failed to insert event, unexpectedly updated ${result.length} rows`
|
|
651
|
-
);
|
|
652
|
-
}
|
|
653
|
-
const [{ id }] = result;
|
|
654
|
-
const notifyResult = await this.#db.select(
|
|
655
|
-
this.#db.raw(`pg_notify(?, ?)`, [TOPIC_PUBLISH, topic])
|
|
656
|
-
);
|
|
657
|
-
if (notifyResult?.length !== 1) {
|
|
658
|
-
this.#logger.warn(
|
|
659
|
-
`Failed to notify subscribers of event with ID '${id}' on topic '${topic}'`
|
|
660
|
-
);
|
|
661
|
-
}
|
|
662
|
-
return { eventId: id };
|
|
663
|
-
}
|
|
664
|
-
async upsertSubscription(id, topics, credentials) {
|
|
665
|
-
const [{ max: maxId }] = await this.#db(TABLE_EVENTS).max("id");
|
|
666
|
-
const result = await this.#db(TABLE_SUBSCRIPTIONS).insert({
|
|
667
|
-
id,
|
|
668
|
-
created_by: creatorId(credentials),
|
|
669
|
-
updated_at: this.#db.fn.now(),
|
|
670
|
-
topics,
|
|
671
|
-
read_until: maxId || 0
|
|
672
|
-
}).onConflict("id").merge(["created_by", "topics", "updated_at"]).returning("*");
|
|
673
|
-
if (result.length !== 1) {
|
|
674
|
-
throw new Error(
|
|
675
|
-
`Failed to upsert subscription, updated ${result.length} rows`
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
async readSubscription(id) {
|
|
680
|
-
const { rows: result } = await this.#db.raw(
|
|
681
|
-
`
|
|
682
|
-
WITH subscription AS (
|
|
683
|
-
SELECT topics, read_until
|
|
684
|
-
FROM event_bus_subscriptions
|
|
685
|
-
WHERE id = :id
|
|
686
|
-
FOR UPDATE
|
|
687
|
-
),
|
|
688
|
-
selected_events AS (
|
|
689
|
-
SELECT event_bus_events.*
|
|
690
|
-
FROM event_bus_events
|
|
691
|
-
INNER JOIN subscription
|
|
692
|
-
ON event_bus_events.topic = ANY(subscription.topics)
|
|
693
|
-
WHERE event_bus_events.id > subscription.read_until
|
|
694
|
-
AND NOT :id = ANY(event_bus_events.notified_subscribers)
|
|
695
|
-
ORDER BY event_bus_events.id ASC LIMIT :limit
|
|
696
|
-
),
|
|
697
|
-
last_event_id AS (
|
|
698
|
-
SELECT max(id) AS last_event_id
|
|
699
|
-
FROM selected_events
|
|
700
|
-
),
|
|
701
|
-
events_array AS (
|
|
702
|
-
SELECT json_agg(row_to_json(selected_events)) AS events
|
|
703
|
-
FROM selected_events
|
|
704
|
-
)
|
|
705
|
-
UPDATE event_bus_subscriptions
|
|
706
|
-
SET read_until = COALESCE(last_event_id, (SELECT MAX(id) FROM event_bus_events), 0)
|
|
707
|
-
FROM events_array, last_event_id
|
|
708
|
-
WHERE event_bus_subscriptions.id = :id
|
|
709
|
-
RETURNING events_array.events
|
|
710
|
-
`,
|
|
711
|
-
{ id, limit: MAX_BATCH_SIZE }
|
|
712
|
-
);
|
|
713
|
-
if (result.length === 0) {
|
|
714
|
-
throw new errors.NotFoundError(`Subscription with ID '${id}' not found`);
|
|
715
|
-
} else if (result.length > 1) {
|
|
716
|
-
throw new Error(
|
|
717
|
-
`Failed to read subscription, unexpectedly updated ${result.length} rows`
|
|
718
|
-
);
|
|
719
|
-
}
|
|
720
|
-
const rows = result[0].events;
|
|
721
|
-
if (!rows || rows.length === 0) {
|
|
722
|
-
return { events: [] };
|
|
723
|
-
}
|
|
724
|
-
return {
|
|
725
|
-
events: rows.map((row) => {
|
|
726
|
-
const { payload, metadata } = JSON.parse(row.data_json);
|
|
727
|
-
return {
|
|
728
|
-
topic: row.topic,
|
|
729
|
-
eventPayload: payload,
|
|
730
|
-
metadata
|
|
731
|
-
};
|
|
732
|
-
})
|
|
733
|
-
};
|
|
734
|
-
}
|
|
735
|
-
async setupListener(subscriptionId, options) {
|
|
736
|
-
const result = await this.#db(TABLE_SUBSCRIPTIONS).select("topics").where({ id: subscriptionId }).first();
|
|
737
|
-
if (!result) {
|
|
738
|
-
throw new errors.NotFoundError(
|
|
739
|
-
`Subscription with ID '${subscriptionId}' not found`
|
|
740
|
-
);
|
|
741
|
-
}
|
|
742
|
-
options.signal.throwIfAborted();
|
|
743
|
-
return this.#listener.setupListener(
|
|
744
|
-
new Set(result.topics ?? []),
|
|
745
|
-
options.signal
|
|
746
|
-
);
|
|
747
|
-
}
|
|
748
|
-
async #cleanup() {
|
|
749
|
-
try {
|
|
750
|
-
const eventCount = await this.#db(TABLE_EVENTS).delete().orWhere(
|
|
751
|
-
(inner) => inner.whereIn(
|
|
752
|
-
"id",
|
|
753
|
-
this.#db.select("id").from(TABLE_EVENTS).orderBy("id", "desc").offset(this.#windowMaxCount)
|
|
754
|
-
).andWhere(
|
|
755
|
-
"created_at",
|
|
756
|
-
"<",
|
|
757
|
-
new Date(Date.now() - this.#windowMinAge)
|
|
758
|
-
)
|
|
759
|
-
).orWhere("created_at", "<", new Date(Date.now() - this.#windowMaxAge));
|
|
760
|
-
if (eventCount > 0) {
|
|
761
|
-
this.#logger.info(
|
|
762
|
-
`Event cleanup resulted in ${eventCount} old events being deleted`
|
|
763
|
-
);
|
|
764
|
-
}
|
|
765
|
-
} catch (error) {
|
|
766
|
-
this.#logger.error("Event cleanup failed", error);
|
|
767
|
-
}
|
|
768
|
-
try {
|
|
769
|
-
const [{ min: minId }] = await this.#db(TABLE_EVENTS).min("id");
|
|
770
|
-
let subscriberCount;
|
|
771
|
-
if (minId === null) {
|
|
772
|
-
subscriberCount = await this.#db(TABLE_SUBSCRIPTIONS).delete();
|
|
773
|
-
} else {
|
|
774
|
-
subscriberCount = await this.#db(TABLE_SUBSCRIPTIONS).delete().where("read_until", "<", minId - 1);
|
|
775
|
-
}
|
|
776
|
-
if (subscriberCount > 0) {
|
|
777
|
-
this.#logger.info(
|
|
778
|
-
`Subscription cleanup resulted in ${subscriberCount} stale subscribers being deleted`
|
|
779
|
-
);
|
|
780
|
-
}
|
|
781
|
-
} catch (error) {
|
|
782
|
-
this.#logger.error("Subscription cleanup failed", error);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
|
|
787
|
-
const DEFAULT_NOTIFY_TIMEOUT_MS = 55e3;
|
|
788
|
-
async function createEventBusStore(deps) {
|
|
789
|
-
const db = await deps.database.getClient();
|
|
790
|
-
if (db.client.config.client === "pg") {
|
|
791
|
-
deps.logger.info("Database is PostgreSQL, using database store");
|
|
792
|
-
return await DatabaseEventBusStore.create(deps);
|
|
793
|
-
}
|
|
794
|
-
deps.logger.info("Database is not PostgreSQL, using memory store");
|
|
795
|
-
return new MemoryEventBusStore();
|
|
796
|
-
}
|
|
797
|
-
async function createEventBusRouter(options) {
|
|
798
|
-
const { httpAuth, notifyTimeoutMs = DEFAULT_NOTIFY_TIMEOUT_MS } = options;
|
|
799
|
-
const logger = options.logger.child({ type: "EventBus" });
|
|
800
|
-
const store = await createEventBusStore(options);
|
|
801
|
-
const apiRouter = await createOpenApiRouter();
|
|
802
|
-
apiRouter.post("/bus/v1/events", async (req, res) => {
|
|
803
|
-
const credentials = await httpAuth.credentials(req, {
|
|
804
|
-
allow: ["service"]
|
|
805
|
-
});
|
|
806
|
-
const topic = req.body.event.topic;
|
|
807
|
-
const notifiedSubscribers = req.body.notifiedSubscribers;
|
|
808
|
-
const result = await store.publish({
|
|
809
|
-
event: {
|
|
810
|
-
topic,
|
|
811
|
-
eventPayload: req.body.event.payload
|
|
812
|
-
},
|
|
813
|
-
notifiedSubscribers,
|
|
814
|
-
credentials
|
|
815
|
-
});
|
|
816
|
-
if (result) {
|
|
817
|
-
logger.debug(
|
|
818
|
-
`Published event to '${topic}' with ID '${result.eventId}'`,
|
|
819
|
-
{
|
|
820
|
-
subject: credentials.principal.subject
|
|
821
|
-
}
|
|
822
|
-
);
|
|
823
|
-
res.status(201).end();
|
|
824
|
-
} else {
|
|
825
|
-
if (notifiedSubscribers) {
|
|
826
|
-
const notified = `'${notifiedSubscribers.join("', '")}'`;
|
|
827
|
-
logger.debug(
|
|
828
|
-
`Skipped publishing of event to '${topic}', subscribers have already been notified: ${notified}`,
|
|
829
|
-
{ subject: credentials.principal.subject }
|
|
830
|
-
);
|
|
831
|
-
} else {
|
|
832
|
-
logger.debug(
|
|
833
|
-
`Skipped publishing of event to '${topic}', no subscribers present`,
|
|
834
|
-
{ subject: credentials.principal.subject }
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
res.status(204).end();
|
|
838
|
-
}
|
|
839
|
-
});
|
|
840
|
-
apiRouter.get(
|
|
841
|
-
"/bus/v1/subscriptions/:subscriptionId/events",
|
|
842
|
-
async (req, res) => {
|
|
843
|
-
const credentials = await httpAuth.credentials(req, {
|
|
844
|
-
allow: ["service"]
|
|
845
|
-
});
|
|
846
|
-
const id = req.params.subscriptionId;
|
|
847
|
-
const controller = new AbortController();
|
|
848
|
-
req.on("end", () => controller.abort());
|
|
849
|
-
const listener = await store.setupListener(id, {
|
|
850
|
-
signal: controller.signal
|
|
851
|
-
});
|
|
852
|
-
const timeout = setTimeout(() => {
|
|
853
|
-
controller.abort();
|
|
854
|
-
}, notifyTimeoutMs);
|
|
855
|
-
try {
|
|
856
|
-
const { events } = await store.readSubscription(id);
|
|
857
|
-
logger.debug(
|
|
858
|
-
`Reading subscription '${id}' resulted in ${events.length} events`,
|
|
859
|
-
{ subject: credentials.principal.subject }
|
|
860
|
-
);
|
|
861
|
-
if (events.length > 0) {
|
|
862
|
-
res.json({
|
|
863
|
-
events: events.map((event) => ({
|
|
864
|
-
topic: event.topic,
|
|
865
|
-
payload: event.eventPayload
|
|
866
|
-
}))
|
|
867
|
-
});
|
|
868
|
-
} else {
|
|
869
|
-
res.status(202);
|
|
870
|
-
res.flushHeaders();
|
|
871
|
-
try {
|
|
872
|
-
const { topic } = await listener.waitForUpdate();
|
|
873
|
-
logger.debug(
|
|
874
|
-
`Received notification for subscription '${id}' for topic '${topic}'`,
|
|
875
|
-
{ subject: credentials.principal.subject }
|
|
876
|
-
);
|
|
877
|
-
} catch (error) {
|
|
878
|
-
if (error !== controller.signal.reason) {
|
|
879
|
-
logger.error(`Error listening for subscription '${id}'`, error);
|
|
880
|
-
}
|
|
881
|
-
} finally {
|
|
882
|
-
await new Promise(
|
|
883
|
-
(resolve) => setTimeout(resolve, 1 + Math.random() * 9)
|
|
884
|
-
);
|
|
885
|
-
res.end();
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
} finally {
|
|
889
|
-
controller.abort();
|
|
890
|
-
clearTimeout(timeout);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
);
|
|
894
|
-
apiRouter.put("/bus/v1/subscriptions/:subscriptionId", async (req, res) => {
|
|
895
|
-
const credentials = await httpAuth.credentials(req, {
|
|
896
|
-
allow: ["service"]
|
|
897
|
-
});
|
|
898
|
-
const id = req.params.subscriptionId;
|
|
899
|
-
await store.upsertSubscription(id, req.body.topics, credentials);
|
|
900
|
-
logger.debug(
|
|
901
|
-
`New subscription '${id}' for topics '${req.body.topics.join("', '")}'`,
|
|
902
|
-
{ subject: credentials.principal.subject }
|
|
903
|
-
);
|
|
904
|
-
res.status(201).end();
|
|
905
|
-
});
|
|
906
|
-
return apiRouter;
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
class EventsExtensionPointImpl {
|
|
910
|
-
#httpPostIngresses = [];
|
|
911
|
-
setEventBroker(_) {
|
|
912
|
-
throw new Error(
|
|
913
|
-
"setEventBroker is not supported anymore; use eventsServiceRef instead"
|
|
914
|
-
);
|
|
915
|
-
}
|
|
916
|
-
addPublishers(_) {
|
|
917
|
-
throw new Error(
|
|
918
|
-
"addPublishers is not supported anymore; use EventsService instead"
|
|
919
|
-
);
|
|
920
|
-
}
|
|
921
|
-
addSubscribers(_) {
|
|
922
|
-
throw new Error(
|
|
923
|
-
"addSubscribers is not supported anymore; use EventsService instead"
|
|
924
|
-
);
|
|
925
|
-
}
|
|
926
|
-
addHttpPostIngress(options) {
|
|
927
|
-
this.#httpPostIngresses.push(options);
|
|
928
|
-
}
|
|
929
|
-
get httpPostIngresses() {
|
|
930
|
-
return this.#httpPostIngresses;
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
const eventsPlugin = backendPluginApi.createBackendPlugin({
|
|
934
|
-
pluginId: "events",
|
|
935
|
-
register(env) {
|
|
936
|
-
const extensionPoint = new EventsExtensionPointImpl();
|
|
937
|
-
env.registerExtensionPoint(alpha.eventsExtensionPoint, extensionPoint);
|
|
938
|
-
env.registerInit({
|
|
939
|
-
deps: {
|
|
940
|
-
config: backendPluginApi.coreServices.rootConfig,
|
|
941
|
-
events: pluginEventsNode.eventsServiceRef,
|
|
942
|
-
database: backendPluginApi.coreServices.database,
|
|
943
|
-
logger: backendPluginApi.coreServices.logger,
|
|
944
|
-
scheduler: backendPluginApi.coreServices.scheduler,
|
|
945
|
-
lifecycle: backendPluginApi.coreServices.lifecycle,
|
|
946
|
-
httpAuth: backendPluginApi.coreServices.httpAuth,
|
|
947
|
-
router: backendPluginApi.coreServices.httpRouter
|
|
948
|
-
},
|
|
949
|
-
async init({
|
|
950
|
-
config,
|
|
951
|
-
events,
|
|
952
|
-
database,
|
|
953
|
-
logger,
|
|
954
|
-
scheduler,
|
|
955
|
-
lifecycle,
|
|
956
|
-
httpAuth,
|
|
957
|
-
router
|
|
958
|
-
}) {
|
|
959
|
-
const ingresses = Object.fromEntries(
|
|
960
|
-
extensionPoint.httpPostIngresses.map((ingress) => [
|
|
961
|
-
ingress.topic,
|
|
962
|
-
ingress
|
|
963
|
-
])
|
|
964
|
-
);
|
|
965
|
-
const http = HttpPostIngressEventPublisher.HttpPostIngressEventPublisher.fromConfig({
|
|
966
|
-
config,
|
|
967
|
-
events,
|
|
968
|
-
ingresses,
|
|
969
|
-
logger
|
|
970
|
-
});
|
|
971
|
-
const eventsRouter = Router__default.default();
|
|
972
|
-
http.bind(eventsRouter);
|
|
973
|
-
router.use(
|
|
974
|
-
await createEventBusRouter({
|
|
975
|
-
database,
|
|
976
|
-
logger,
|
|
977
|
-
httpAuth,
|
|
978
|
-
scheduler,
|
|
979
|
-
lifecycle
|
|
980
|
-
})
|
|
981
|
-
);
|
|
982
|
-
router.use(eventsRouter);
|
|
983
|
-
router.addAuthPolicy({
|
|
984
|
-
allow: "unauthenticated",
|
|
985
|
-
path: "/http"
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
});
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
|
|
992
|
-
exports.default = eventsPlugin;
|
|
9
|
+
exports.default = EventsPlugin.eventsPlugin;
|
|
993
10
|
//# sourceMappingURL=alpha.cjs.js.map
|