@cap-js-community/event-queue 1.10.0-beta.5 → 1.10.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/event-queue",
|
|
3
|
-
"version": "1.10.0
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "An event queue that enables secure transactional processing of asynchronous and periodic events, featuring instant event processing with Redis Pub/Sub and load distribution across all application instances.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
|
@@ -44,17 +44,17 @@
|
|
|
44
44
|
"node": ">=18"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@sap/xssec": "^4.
|
|
48
|
-
"cron-parser": "^5.
|
|
47
|
+
"@sap/xssec": "^4.6.0",
|
|
48
|
+
"cron-parser": "^5.1.0",
|
|
49
49
|
"redis": "^4.7.0",
|
|
50
50
|
"verror": "^1.10.1",
|
|
51
|
-
"yaml": "^2.
|
|
51
|
+
"yaml": "^2.7.1"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@cap-js/cds-test": "^0.
|
|
55
|
-
"@cap-js/hana": "^1.
|
|
56
|
-
"@cap-js/sqlite": "^1.
|
|
57
|
-
"@sap/cds": "^8.
|
|
54
|
+
"@cap-js/cds-test": "^0.3.0",
|
|
55
|
+
"@cap-js/hana": "^1.8.0",
|
|
56
|
+
"@cap-js/sqlite": "^1.10.0",
|
|
57
|
+
"@sap/cds": "^8.9.0",
|
|
58
58
|
"@sap/cds-dk": "^8.8.0",
|
|
59
59
|
"eslint": "^8.57.0",
|
|
60
60
|
"eslint-config-prettier": "^9.1.0",
|
package/src/config.js
CHANGED
|
@@ -153,18 +153,50 @@ class Config {
|
|
|
153
153
|
return !!this.#env.redisRequires?.credentials;
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
#parseRegexOrString(str) {
|
|
157
|
+
const regexLiteralPattern = /^\/((?:\\.|[^\\/])*)\/([gimsuy]*)$/;
|
|
158
|
+
const match = str.match(regexLiteralPattern);
|
|
159
|
+
|
|
160
|
+
if (match) {
|
|
161
|
+
try {
|
|
162
|
+
return { type: "regex", value: new RegExp(match[1], match[2]) };
|
|
163
|
+
} catch {
|
|
164
|
+
return { type: "string", value: str };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { type: "string", value: str };
|
|
168
|
+
}
|
|
169
|
+
|
|
156
170
|
shouldBeProcessedInThisApplication(type, subType) {
|
|
157
171
|
const config = this.#eventMap[this.generateKey(type, subType)];
|
|
158
172
|
const appNameConfig = config._appNameMap;
|
|
159
173
|
const appInstanceConfig = config._appInstancesMap;
|
|
174
|
+
let result = true;
|
|
160
175
|
if (!appNameConfig && !appInstanceConfig) {
|
|
161
|
-
return
|
|
176
|
+
return result;
|
|
162
177
|
}
|
|
163
178
|
|
|
164
179
|
if (appNameConfig) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
180
|
+
if (config._appNameContainsRegex) {
|
|
181
|
+
for (const configKey in appNameConfig) {
|
|
182
|
+
const config = appNameConfig[configKey];
|
|
183
|
+
if (config.type === "regex") {
|
|
184
|
+
result = config.value.test(this.#env.applicationName);
|
|
185
|
+
} else {
|
|
186
|
+
const shouldBeProcessedBasedOnAppName = appNameConfig[this.#env.applicationName];
|
|
187
|
+
if (!shouldBeProcessedBasedOnAppName) {
|
|
188
|
+
result = config.value === this.#env.applicationName;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (result) {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
const shouldBeProcessedBasedOnAppName = appNameConfig[this.#env.applicationName];
|
|
197
|
+
if (!shouldBeProcessedBasedOnAppName) {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
168
200
|
}
|
|
169
201
|
}
|
|
170
202
|
|
|
@@ -175,7 +207,7 @@ class Config {
|
|
|
175
207
|
}
|
|
176
208
|
}
|
|
177
209
|
|
|
178
|
-
return
|
|
210
|
+
return result;
|
|
179
211
|
}
|
|
180
212
|
|
|
181
213
|
checkRedisEnabled() {
|
|
@@ -530,7 +562,12 @@ class Config {
|
|
|
530
562
|
}
|
|
531
563
|
|
|
532
564
|
#basicEventTransformationAfterValidate(event) {
|
|
533
|
-
event._appNameMap = event.appNames
|
|
565
|
+
event._appNameMap = event.appNames
|
|
566
|
+
? Object.fromEntries(new Map(event.appNames.map((a) => [a, this.#parseRegexOrString(a)])))
|
|
567
|
+
: null;
|
|
568
|
+
event._appNameContainsRegex = event.appNames
|
|
569
|
+
? event.appNames.some((appName) => this.#parseRegexOrString(appName).type === "regex")
|
|
570
|
+
: null;
|
|
534
571
|
event._appInstancesMap = event.appInstances
|
|
535
572
|
? Object.fromEntries(new Map(event.appInstances.map((a) => [a, true])))
|
|
536
573
|
: null;
|
|
@@ -9,6 +9,12 @@ const config = require("../config");
|
|
|
9
9
|
|
|
10
10
|
const COMPONENT_NAME = "/eventQueue/outbox/generic";
|
|
11
11
|
|
|
12
|
+
const EVENT_QUEUE_ACTIONS = {
|
|
13
|
+
EXCEEDED: "eventQueueRetriesExceeded",
|
|
14
|
+
CLUSTER: "eventQueueCluster",
|
|
15
|
+
CHECK_AND_ADJUST: "eventQueueCheckAndAdjustPayload",
|
|
16
|
+
};
|
|
17
|
+
|
|
12
18
|
class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
13
19
|
constructor(context, eventType, eventSubType, config) {
|
|
14
20
|
super(context, eventType, eventSubType, config);
|
|
@@ -21,7 +27,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
21
27
|
this.__srvUnboxed = cds.unboxed(this.__srv);
|
|
22
28
|
const { handlers, clusterRelevant, specificClusterRelevant } = this.__srvUnboxed.handlers.on.reduce(
|
|
23
29
|
(result, handler) => {
|
|
24
|
-
if (handler.on.startsWith(
|
|
30
|
+
if (handler.on.startsWith(EVENT_QUEUE_ACTIONS.CLUSTER)) {
|
|
25
31
|
if (handler.on.split(".").length === 2) {
|
|
26
32
|
result.specificClusterRelevant = true;
|
|
27
33
|
} else {
|
|
@@ -41,8 +47,6 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
41
47
|
return await super.getQueueEntriesAndSetToInProgress();
|
|
42
48
|
}
|
|
43
49
|
|
|
44
|
-
// document structure is a map of { key: { queueEntries: [], payload: {} }
|
|
45
|
-
// TODO: document that clusterQueueEntries is now async!!!
|
|
46
50
|
async clusterQueueEntries(queueEntriesWithPayloadMap) {
|
|
47
51
|
if (!this.__genericClusterRelevantAndAvailable && !this.__specificClusterRelevantAndAvailable) {
|
|
48
52
|
return super.clusterQueueEntries(queueEntriesWithPayloadMap);
|
|
@@ -58,7 +62,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
58
62
|
} else {
|
|
59
63
|
for (const actionName in genericClusterEvents) {
|
|
60
64
|
const msg = new cds.Request({
|
|
61
|
-
event:
|
|
65
|
+
event: EVENT_QUEUE_ACTIONS.CLUSTER,
|
|
62
66
|
user: this.context.user,
|
|
63
67
|
eventQueue: {
|
|
64
68
|
processor: this,
|
|
@@ -67,7 +71,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
67
71
|
clusterByEventProperty: (propertyName, cb) =>
|
|
68
72
|
this.#clusterByEventProperty(actionName, genericClusterEvents[actionName], propertyName, cb),
|
|
69
73
|
clusterByDataProperty: (propertyName, cb) =>
|
|
70
|
-
this.#clusterByDataProperty(actionName,
|
|
74
|
+
this.#clusterByDataProperty(actionName, genericClusterEvents[actionName], propertyName, cb),
|
|
71
75
|
},
|
|
72
76
|
});
|
|
73
77
|
const clusterResult = await this.__srvUnboxed.tx(this.context).send(msg);
|
|
@@ -89,7 +93,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
89
93
|
|
|
90
94
|
for (const actionName in specificClusterEvents) {
|
|
91
95
|
const msg = new cds.Request({
|
|
92
|
-
event:
|
|
96
|
+
event: `${EVENT_QUEUE_ACTIONS.CLUSTER}.${actionName}`,
|
|
93
97
|
user: this.context.user,
|
|
94
98
|
eventQueue: {
|
|
95
99
|
processor: this,
|
|
@@ -249,13 +253,13 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
#hasEventSpecificClusterHandler(queueEntry) {
|
|
252
|
-
return !!this.__onHandlers[[
|
|
256
|
+
return !!this.__onHandlers[[EVENT_QUEUE_ACTIONS.CLUSTER, queueEntry.payload.event].join(".")];
|
|
253
257
|
}
|
|
254
258
|
|
|
255
259
|
async checkEventAndGeneratePayload(queueEntry) {
|
|
256
260
|
const payload = await super.checkEventAndGeneratePayload(queueEntry);
|
|
257
261
|
const { event } = payload;
|
|
258
|
-
const handlerName = this.#checkHandlerExists(
|
|
262
|
+
const handlerName = this.#checkHandlerExists(EVENT_QUEUE_ACTIONS.CHECK_AND_ADJUST, event);
|
|
259
263
|
if (!handlerName) {
|
|
260
264
|
return payload;
|
|
261
265
|
}
|
|
@@ -276,7 +280,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
276
280
|
|
|
277
281
|
async hookForExceededEvents(exceededEvent) {
|
|
278
282
|
const { event } = exceededEvent.payload;
|
|
279
|
-
const handlerName = this.#checkHandlerExists(
|
|
283
|
+
const handlerName = this.#checkHandlerExists(EVENT_QUEUE_ACTIONS.EXCEEDED, event);
|
|
280
284
|
if (!handlerName) {
|
|
281
285
|
return await super.hookForExceededEvents(exceededEvent);
|
|
282
286
|
}
|
package/src/publishEvent.js
CHANGED
|
@@ -63,7 +63,7 @@ const publishEvent = async (
|
|
|
63
63
|
event.context = JSON.stringify({ traceContext: openTelemetry.getCurrentTraceContext() });
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
if (eventConfig.timeBucket &&
|
|
66
|
+
if (eventConfig.timeBucket && !(startAfter in event)) {
|
|
67
67
|
event.startAfter = CronExpressionParser.parse(eventConfig.timeBucket).next().toISOString();
|
|
68
68
|
}
|
|
69
69
|
}
|