@cap-js-community/event-queue 1.1.0 → 1.2.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/cds-plugin.js +4 -4
- package/package.json +4 -4
- package/src/config.js +9 -0
- package/src/initialize.js +30 -22
- package/src/redisPubSub.js +4 -0
- package/src/runner.js +7 -0
- package/src/shared/cdsHelper.js +4 -2
package/cds-plugin.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
const cds = require("@sap/cds");
|
|
4
4
|
|
|
5
5
|
const eventQueue = require("./src");
|
|
6
|
+
const COMPONENT_NAME = "/eventQueue/plugin";
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
});
|
|
8
|
+
const eventQueueConfig = cds.env.eventQueue;
|
|
9
|
+
if (!(cds.build.register || (!eventQueueConfig?.config && !eventQueueConfig?.configFilePath))) {
|
|
10
|
+
eventQueue.initialize().catch((err) => cds.log(COMPONENT_NAME).error(err));
|
|
11
11
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/event-queue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "An event queue that enables secure transactional processing of asynchronous events, featuring instant event processing with Redis Pub/Sub and load distribution across all application instances.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
"yaml": "2.3.4"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@sap/cds": "7.5.
|
|
51
|
-
"@sap/cds-dk": "7.5.1",
|
|
50
|
+
"@sap/cds": "^7.5.3",
|
|
51
|
+
"@sap/cds-dk": "^7.5.1",
|
|
52
52
|
"eslint": "8.56.0",
|
|
53
53
|
"eslint-config-prettier": "9.1.0",
|
|
54
|
-
"eslint-plugin-jest": "27.6.
|
|
54
|
+
"eslint-plugin-jest": "27.6.3",
|
|
55
55
|
"eslint-plugin-node": "11.1.0",
|
|
56
56
|
"express": "4.18.2",
|
|
57
57
|
"hdb": "0.19.7",
|
package/src/config.js
CHANGED
|
@@ -55,6 +55,7 @@ class Config {
|
|
|
55
55
|
#isPeriodicEventBlockedCb;
|
|
56
56
|
#thresholdLoggingEventProcessing;
|
|
57
57
|
#useAsCAPOutbox;
|
|
58
|
+
#userId;
|
|
58
59
|
static #instance;
|
|
59
60
|
constructor() {
|
|
60
61
|
this.#logger = cds.log(COMPONENT_NAME);
|
|
@@ -453,6 +454,14 @@ class Config {
|
|
|
453
454
|
return this.#useAsCAPOutbox;
|
|
454
455
|
}
|
|
455
456
|
|
|
457
|
+
set userId(value) {
|
|
458
|
+
this.#userId = value;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
get userId() {
|
|
462
|
+
return this.#userId;
|
|
463
|
+
}
|
|
464
|
+
|
|
456
465
|
get isMultiTenancy() {
|
|
457
466
|
return !!cds.requires.multitenancy;
|
|
458
467
|
}
|
package/src/initialize.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
const { promisify } = require("util");
|
|
4
4
|
const fs = require("fs");
|
|
5
|
-
const path = require("path");
|
|
6
5
|
|
|
7
6
|
const cds = require("@sap/cds");
|
|
8
7
|
const yaml = require("yaml");
|
|
@@ -37,6 +36,7 @@ const CONFIG_VARS = [
|
|
|
37
36
|
["updatePeriodicEvents", true],
|
|
38
37
|
["thresholdLoggingEventProcessing", 50],
|
|
39
38
|
["useAsCAPOutbox", false],
|
|
39
|
+
["userId", null],
|
|
40
40
|
];
|
|
41
41
|
|
|
42
42
|
const initialize = async ({
|
|
@@ -52,6 +52,7 @@ const initialize = async ({
|
|
|
52
52
|
updatePeriodicEvents,
|
|
53
53
|
thresholdLoggingEventProcessing,
|
|
54
54
|
useAsCAPOutbox,
|
|
55
|
+
userId,
|
|
55
56
|
} = {}) => {
|
|
56
57
|
// TODO: initialize check:
|
|
57
58
|
// - content of yaml check
|
|
@@ -74,19 +75,22 @@ const initialize = async ({
|
|
|
74
75
|
skipCsnCheck,
|
|
75
76
|
updatePeriodicEvents,
|
|
76
77
|
thresholdLoggingEventProcessing,
|
|
77
|
-
useAsCAPOutbox
|
|
78
|
+
useAsCAPOutbox,
|
|
79
|
+
userId
|
|
78
80
|
);
|
|
79
81
|
|
|
80
82
|
const logger = cds.log(COMPONENT);
|
|
81
83
|
config.fileContent = await readConfigFromFile(config.configFilePath);
|
|
82
84
|
config.checkRedisEnabled();
|
|
83
85
|
|
|
84
|
-
const dbService = await cds.connect.to("db");
|
|
85
|
-
await (cds.model ? Promise.resolve() : new Promise((resolve) => cds.on("serving", resolve)));
|
|
86
|
-
!config.skipCsnCheck && (await csnCheck());
|
|
87
86
|
if (config.processEventsAfterPublish) {
|
|
88
|
-
|
|
87
|
+
cds.on("connect", (service) => {
|
|
88
|
+
if (service.name === "db ") {
|
|
89
|
+
dbHandler.registerEventQueueDbHandler(service);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
89
92
|
}
|
|
93
|
+
!config.skipCsnCheck && (await csnCheck());
|
|
90
94
|
|
|
91
95
|
monkeyPatchCAPOutbox();
|
|
92
96
|
registerEventProcessors();
|
|
@@ -150,26 +154,30 @@ const monkeyPatchCAPOutbox = () => {
|
|
|
150
154
|
};
|
|
151
155
|
|
|
152
156
|
const csnCheck = async () => {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
+
cds.on("loaded", async (csn) => {
|
|
158
|
+
if (csn.namespace === "cds.xt") {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const eventCsn = csn.definitions[config.tableNameEventQueue];
|
|
162
|
+
if (!eventCsn) {
|
|
163
|
+
throw EventQueueError.missingTableInCsn(config.tableNameEventQueue);
|
|
164
|
+
}
|
|
157
165
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
166
|
+
const lockCsn = csn.definitions[config.tableNameEventLock];
|
|
167
|
+
if (!lockCsn) {
|
|
168
|
+
throw EventQueueError.missingTableInCsn(config.tableNameEventLock);
|
|
169
|
+
}
|
|
162
170
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
if (config.tableNameEventQueue === BASE_TABLES.EVENT && config.tableNameEventLock === BASE_TABLES.LOCK) {
|
|
172
|
+
return; // no need to check base tables
|
|
173
|
+
}
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const baseLock = csn.definitions["sap.eventqueue.Lock"];
|
|
175
|
+
const baseEvent = csn.definitions["sap.eventqueue.Event"];
|
|
176
|
+
const baseLock = csn.definitions["sap.eventqueue.Lock"];
|
|
170
177
|
|
|
171
|
-
|
|
172
|
-
|
|
178
|
+
checkCustomTable(baseEvent, eventCsn);
|
|
179
|
+
checkCustomTable(baseLock, lockCsn);
|
|
180
|
+
});
|
|
173
181
|
};
|
|
174
182
|
|
|
175
183
|
const checkCustomTable = (baseCsn, customCsn) => {
|
package/src/redisPubSub.js
CHANGED
|
@@ -38,8 +38,10 @@ const _messageHandlerProcessEvents = async (messageData) => {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
41
|
+
const user = new cds.User.Privileged(config.userId);
|
|
41
42
|
const tenantContext = {
|
|
42
43
|
tenant: tenantId,
|
|
44
|
+
user,
|
|
43
45
|
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
44
46
|
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
45
47
|
};
|
|
@@ -90,9 +92,11 @@ const broadcastEvent = async (tenantId, type, subType) => {
|
|
|
90
92
|
let context = {};
|
|
91
93
|
if (tenantId) {
|
|
92
94
|
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
95
|
+
const user = new cds.User.Privileged(config.userId);
|
|
93
96
|
context = {
|
|
94
97
|
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
95
98
|
tenant: tenantId,
|
|
99
|
+
user,
|
|
96
100
|
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
97
101
|
};
|
|
98
102
|
}
|
package/src/runner.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const { randomUUID } = require("crypto");
|
|
4
4
|
|
|
5
|
+
const cds = require("@sap/cds");
|
|
6
|
+
|
|
5
7
|
const eventQueueConfig = require("./config");
|
|
6
8
|
const { processEventQueue } = require("./processEventQueue");
|
|
7
9
|
const WorkerQueue = require("./shared/WorkerQueue");
|
|
@@ -11,6 +13,7 @@ const SetIntervalDriftSafe = require("./shared/SetIntervalDriftSafe");
|
|
|
11
13
|
const { getSubdomainForTenantId } = require("./shared/cdsHelper");
|
|
12
14
|
const periodicEvents = require("./periodicEvents");
|
|
13
15
|
const { hashStringTo32Bit } = require("./shared/common");
|
|
16
|
+
const config = require("./config");
|
|
14
17
|
|
|
15
18
|
const COMPONENT_NAME = "/eventQueue/runner";
|
|
16
19
|
const EVENT_QUEUE_RUN_ID = "EVENT_QUEUE_RUN_ID";
|
|
@@ -107,8 +110,10 @@ const _executeEventsAllTenants = (tenantIds, runId) => {
|
|
|
107
110
|
|
|
108
111
|
return product.map(async ([tenantId, event]) => {
|
|
109
112
|
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
113
|
+
const user = new cds.User.Privileged(config.userId);
|
|
110
114
|
const tenantContext = {
|
|
111
115
|
tenant: tenantId,
|
|
116
|
+
user,
|
|
112
117
|
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
113
118
|
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
114
119
|
};
|
|
@@ -140,8 +145,10 @@ const _executePeriodicEventsAllTenants = (tenantIds, runId) => {
|
|
|
140
145
|
WorkerQueue.instance.addToQueue(1, label, async () => {
|
|
141
146
|
try {
|
|
142
147
|
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
148
|
+
const user = new cds.User.Privileged(config.userId);
|
|
143
149
|
const tenantContext = {
|
|
144
150
|
tenant: tenantId,
|
|
151
|
+
user,
|
|
145
152
|
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
146
153
|
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
147
154
|
};
|
package/src/shared/cdsHelper.js
CHANGED
|
@@ -24,13 +24,14 @@ async function executeInNewTransaction(context = {}, transactionTag, fn, args, {
|
|
|
24
24
|
const parameters = Array.isArray(args) ? args : [args];
|
|
25
25
|
const logger = cds.log(COMPONENT_NAME);
|
|
26
26
|
try {
|
|
27
|
+
const user = new cds.User.Privileged(config.userId);
|
|
27
28
|
if (cds.db.kind === "hana") {
|
|
28
29
|
await cds.tx(
|
|
29
30
|
{
|
|
30
31
|
id: context.id,
|
|
31
32
|
tenant: context.tenant,
|
|
32
33
|
locale: context.locale,
|
|
33
|
-
user
|
|
34
|
+
user,
|
|
34
35
|
headers: context.headers,
|
|
35
36
|
http: context.http,
|
|
36
37
|
},
|
|
@@ -48,13 +49,14 @@ async function executeInNewTransaction(context = {}, transactionTag, fn, args, {
|
|
|
48
49
|
id: context.id,
|
|
49
50
|
tenant: context.tenant,
|
|
50
51
|
locale: context.locale,
|
|
51
|
-
user
|
|
52
|
+
user,
|
|
52
53
|
headers: context.headers,
|
|
53
54
|
http: context.http,
|
|
54
55
|
},
|
|
55
56
|
async (tx) => fn(tx, ...parameters)
|
|
56
57
|
);
|
|
57
58
|
} else {
|
|
59
|
+
contextTx.context.user = user;
|
|
58
60
|
await fn(contextTx, ...parameters);
|
|
59
61
|
}
|
|
60
62
|
}
|