@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 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
- if (cds.env.eventQueue && cds.env.eventQueue.plugin) {
8
- cds.on("serving", async () => {
9
- await eventQueue.initialize();
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.1.0",
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.2",
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.2",
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
- dbHandler.registerEventQueueDbHandler(dbService);
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
- const eventCsn = cds.model.definitions[config.tableNameEventQueue];
154
- if (!eventCsn) {
155
- throw EventQueueError.missingTableInCsn(config.tableNameEventQueue);
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
- const lockCsn = cds.model.definitions[config.tableNameEventLock];
159
- if (!lockCsn) {
160
- throw EventQueueError.missingTableInCsn(config.tableNameEventLock);
161
- }
166
+ const lockCsn = csn.definitions[config.tableNameEventLock];
167
+ if (!lockCsn) {
168
+ throw EventQueueError.missingTableInCsn(config.tableNameEventLock);
169
+ }
162
170
 
163
- if (config.tableNameEventQueue === BASE_TABLES.EVENT && config.tableNameEventLock === BASE_TABLES.LOCK) {
164
- return; // no need to check base tables
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
- const csn = await cds.load(path.join(__dirname, "..", "db"));
168
- const baseEvent = csn.definitions["sap.eventqueue.Event"];
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
- checkCustomTable(baseEvent, eventCsn);
172
- checkCustomTable(baseLock, lockCsn);
178
+ checkCustomTable(baseEvent, eventCsn);
179
+ checkCustomTable(baseLock, lockCsn);
180
+ });
173
181
  };
174
182
 
175
183
  const checkCustomTable = (baseCsn, customCsn) => {
@@ -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
  };
@@ -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: context.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: context.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
  }