@cap-js-community/event-queue 0.1.49
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/LICENSE +201 -0
- package/README.md +160 -0
- package/cds-plugin.js +11 -0
- package/db/Event.cds +24 -0
- package/db/Lock.cds +10 -0
- package/db/index.cds +2 -0
- package/index.cds +1 -0
- package/package.json +55 -0
- package/src/EventQueueError.js +141 -0
- package/src/EventQueueProcessorBase.js +922 -0
- package/src/config.js +196 -0
- package/src/constants.js +11 -0
- package/src/dbHandler.js +40 -0
- package/src/index.js +20 -0
- package/src/initialize.js +216 -0
- package/src/processEventQueue.js +297 -0
- package/src/publishEvent.js +25 -0
- package/src/redisPubSub.js +143 -0
- package/src/runner.js +237 -0
- package/src/shared/PerformanceTracer.js +74 -0
- package/src/shared/WorkerQueue.js +78 -0
- package/src/shared/cdsHelper.js +136 -0
- package/src/shared/common.js +115 -0
- package/src/shared/distributedLock.js +191 -0
- package/src/shared/env.js +9 -0
- package/src/shared/redis.js +70 -0
package/src/config.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const cds = require("@sap/cds");
|
|
4
|
+
|
|
5
|
+
const env = require("./shared/env");
|
|
6
|
+
|
|
7
|
+
let instance;
|
|
8
|
+
|
|
9
|
+
const FOR_UPDATE_TIMEOUT = 10;
|
|
10
|
+
const GLOBAL_TX_TIMEOUT = 30 * 60 * 1000;
|
|
11
|
+
|
|
12
|
+
class Config {
|
|
13
|
+
constructor() {
|
|
14
|
+
this.__config = null;
|
|
15
|
+
this.__forUpdateTimeout = FOR_UPDATE_TIMEOUT;
|
|
16
|
+
this.__globalTxTimeout = GLOBAL_TX_TIMEOUT;
|
|
17
|
+
this.__runInterval = null;
|
|
18
|
+
this.__redisEnabled = null;
|
|
19
|
+
this.__isOnCF = env.isOnCF;
|
|
20
|
+
this.__initialized = false;
|
|
21
|
+
this.__parallelTenantProcessing = null;
|
|
22
|
+
this.__tableNameEventQueue = null;
|
|
23
|
+
this.__tableNameEventLock = null;
|
|
24
|
+
this.__vcapServices = this._parseVcapServices();
|
|
25
|
+
this.__isRunnerDeactivated = false;
|
|
26
|
+
this.__eventsForAutomaticRun = null;
|
|
27
|
+
this.__configFilePath = null;
|
|
28
|
+
this.__processEventsAfterPublish = null;
|
|
29
|
+
this.__skipCsnCheck = null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
getEventConfig(type, subType) {
|
|
33
|
+
return this.__eventMap[[type, subType].join("##")];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
hasEventAfterCommitFlag(type, subType) {
|
|
37
|
+
return (
|
|
38
|
+
this.__eventMap[[type, subType].join("##")]?.processAfterCommit ?? true
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
_checkRedisIsBound() {
|
|
43
|
+
return !!this.getRedisCredentialsFromEnv();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
getRedisCredentialsFromEnv() {
|
|
47
|
+
return this.__vcapServices["redis-cache"]?.[0]?.credentials;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
_parseVcapServices() {
|
|
51
|
+
try {
|
|
52
|
+
return JSON.parse(process.env.VCAP_SERVICES);
|
|
53
|
+
} catch {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
calculateIsRedisEnabled() {
|
|
59
|
+
this.__redisEnabled = this._checkRedisIsBound() && this.__isOnCF;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
get isRunnerDeactivated() {
|
|
63
|
+
return this.__isRunnerDeactivated;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
set isRunnerDeactivated(value) {
|
|
67
|
+
this.__isRunnerDeactivated = value;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
set fileContent(config) {
|
|
71
|
+
this.__config = config;
|
|
72
|
+
this.__eventMap = config.events.reduce((result, event) => {
|
|
73
|
+
result[[event.type, event.subType].join("##")] = event;
|
|
74
|
+
return result;
|
|
75
|
+
}, {});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get fileContent() {
|
|
79
|
+
return this.__config;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
get events() {
|
|
83
|
+
return this.__config.events;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get forUpdateTimeout() {
|
|
87
|
+
return this.__forUpdateTimeout;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get globalTxTimeout() {
|
|
91
|
+
return this.__globalTxTimeout;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
set forUpdateTimeout(value) {
|
|
95
|
+
this.__forUpdateTimeout = value;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
set globalTxTimeout(value) {
|
|
99
|
+
this.__globalTxTimeout = value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
get runInterval() {
|
|
103
|
+
return this.__runInterval;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
set runInterval(value) {
|
|
107
|
+
this.__runInterval = value;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
get redisEnabled() {
|
|
111
|
+
return this.__redisEnabled;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
set redisEnabled(value) {
|
|
115
|
+
this.__redisEnabled = value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
get isOnCF() {
|
|
119
|
+
return this.__isOnCF;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
set isOnCF(value) {
|
|
123
|
+
this.__isOnCF = value;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
get initialized() {
|
|
127
|
+
return this.__initialized;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
set initialized(value) {
|
|
131
|
+
this.__initialized = value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
get parallelTenantProcessing() {
|
|
135
|
+
return this.__parallelTenantProcessing;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
set parallelTenantProcessing(value) {
|
|
139
|
+
this.__parallelTenantProcessing = value;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
get tableNameEventQueue() {
|
|
143
|
+
return this.__tableNameEventQueue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
set tableNameEventQueue(value) {
|
|
147
|
+
this.__tableNameEventQueue = value;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
get tableNameEventLock() {
|
|
151
|
+
return this.__tableNameEventLock;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
set tableNameEventLock(value) {
|
|
155
|
+
this.__tableNameEventLock = value;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
set configFilePath(value) {
|
|
159
|
+
this.__configFilePath = value;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
get configFilePath() {
|
|
163
|
+
return this.__configFilePath;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
set processEventsAfterPublish(value) {
|
|
167
|
+
this.__processEventsAfterPublish = value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
get processEventsAfterPublish() {
|
|
171
|
+
return this.__processEventsAfterPublish;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
set skipCsnCheck(value) {
|
|
175
|
+
this.__skipCsnCheck = value;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
get skipCsnCheck() {
|
|
179
|
+
return this.__skipCsnCheck;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
get isMultiTenancy() {
|
|
183
|
+
return !!cds.requires.multitenancy;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const getConfigInstance = () => {
|
|
188
|
+
if (!instance) {
|
|
189
|
+
instance = new Config();
|
|
190
|
+
}
|
|
191
|
+
return instance;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
module.exports = {
|
|
195
|
+
getConfigInstance,
|
|
196
|
+
};
|
package/src/constants.js
ADDED
package/src/dbHandler.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { publishEvent } = require("./redisPubSub");
|
|
4
|
+
const config = require("./config");
|
|
5
|
+
|
|
6
|
+
const registerEventQueueDbHandler = (dbService) => {
|
|
7
|
+
const configInstance = config.getConfigInstance();
|
|
8
|
+
const def = dbService.model.definitions[configInstance.tableNameEventQueue];
|
|
9
|
+
dbService.after("CREATE", def, (_, req) => {
|
|
10
|
+
req.tx._ = req.tx._ ?? {};
|
|
11
|
+
req.tx._.eventQueuePublishEvents = req.tx._.eventQueuePublishEvents ?? {};
|
|
12
|
+
const eventQueuePublishEvents = req.tx._.eventQueuePublishEvents;
|
|
13
|
+
const data = Array.isArray(req.data) ? req.data : [req.data];
|
|
14
|
+
const eventCombinations = Object.keys(
|
|
15
|
+
data.reduce((result, event) => {
|
|
16
|
+
const key = [event.type, event.subType].join("##");
|
|
17
|
+
if (
|
|
18
|
+
!configInstance.hasEventAfterCommitFlag(event.type, event.subType) ||
|
|
19
|
+
eventQueuePublishEvents[key]
|
|
20
|
+
) {
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
eventQueuePublishEvents[key] = true;
|
|
24
|
+
result[key] = true;
|
|
25
|
+
return result;
|
|
26
|
+
}, {})
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
eventCombinations.length &&
|
|
30
|
+
req.on("succeeded", () => {
|
|
31
|
+
for (const eventCombination of eventCombinations) {
|
|
32
|
+
publishEvent(req.tenant, ...eventCombination.split("##"));
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
registerEventQueueDbHandler,
|
|
40
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// TODO: how to deal with fatal logs
|
|
4
|
+
// TODO: think about situations where isInitialized need to be checked - publishEvent access config which is not initialized
|
|
5
|
+
// TODO: add tests for config --> similar to csn check
|
|
6
|
+
// TODO: redis client check reconnect strategy
|
|
7
|
+
|
|
8
|
+
// TODO: for test
|
|
9
|
+
// --> deeper look into the functions e.g. getQueueEntriesAndSetToInProgress
|
|
10
|
+
// TODO: add test for commit on event level and stuff like that
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
...require("./initialize"),
|
|
14
|
+
...require("./config"),
|
|
15
|
+
...require("./processEventQueue"),
|
|
16
|
+
...require("./dbHandler"),
|
|
17
|
+
...require("./constants"),
|
|
18
|
+
...require("./publishEvent"),
|
|
19
|
+
EventQueueProcessorBase: require("./EventQueueProcessorBase"),
|
|
20
|
+
};
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { promisify } = require("util");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const cds = require("@sap/cds");
|
|
8
|
+
|
|
9
|
+
const yaml = require("yaml");
|
|
10
|
+
const VError = require("verror");
|
|
11
|
+
|
|
12
|
+
const EventQueueError = require("./EventQueueError");
|
|
13
|
+
const runner = require("./runner");
|
|
14
|
+
const dbHandler = require("./dbHandler");
|
|
15
|
+
const { getConfigInstance } = require("./config");
|
|
16
|
+
const { initEventQueueRedisSubscribe } = require("./redisPubSub");
|
|
17
|
+
|
|
18
|
+
const readFileAsync = promisify(fs.readFile);
|
|
19
|
+
|
|
20
|
+
const VERROR_CLUSTER_NAME = "EventQueueInitialization";
|
|
21
|
+
const COMPONENT = "eventQueue/initialize";
|
|
22
|
+
const BASE_TABLES = {
|
|
23
|
+
EVENT: "sap.eventqueue.Event",
|
|
24
|
+
LOCK: "sap.eventqueue.Lock",
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const initialize = async ({
|
|
28
|
+
configFilePath,
|
|
29
|
+
registerAsEventProcessor,
|
|
30
|
+
processEventsAfterPublish,
|
|
31
|
+
runInterval,
|
|
32
|
+
parallelTenantProcessing,
|
|
33
|
+
tableNameEventQueue,
|
|
34
|
+
tableNameEventLock,
|
|
35
|
+
skipCsnCheck,
|
|
36
|
+
} = {}) => {
|
|
37
|
+
// TODO: initialize check:
|
|
38
|
+
// - content of yaml check
|
|
39
|
+
// - betweenRuns and parallelTenantProcessing
|
|
40
|
+
|
|
41
|
+
const configInstance = getConfigInstance();
|
|
42
|
+
if (configInstance.initialized) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
configInstance.initialized = true;
|
|
46
|
+
|
|
47
|
+
mixConfigVarsWithEnv(
|
|
48
|
+
configFilePath,
|
|
49
|
+
registerAsEventProcessor,
|
|
50
|
+
processEventsAfterPublish,
|
|
51
|
+
runInterval,
|
|
52
|
+
parallelTenantProcessing,
|
|
53
|
+
tableNameEventQueue,
|
|
54
|
+
tableNameEventLock,
|
|
55
|
+
skipCsnCheck
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const logger = cds.log(COMPONENT);
|
|
59
|
+
configInstance.fileContent = await readConfigFromFile(
|
|
60
|
+
configInstance.configFilePath
|
|
61
|
+
);
|
|
62
|
+
configInstance.calculateIsRedisEnabled();
|
|
63
|
+
|
|
64
|
+
const dbService = await cds.connect.to("db");
|
|
65
|
+
await (cds.model
|
|
66
|
+
? Promise.resolve()
|
|
67
|
+
: new Promise((resolve) => cds.on("serving", resolve)));
|
|
68
|
+
!configInstance.skipCsnCheck && (await csnCheck());
|
|
69
|
+
if (configInstance.processEventsAfterPublish) {
|
|
70
|
+
// TODO: remove this as soon as CDS fixes the current plugin model issues --> cds 7
|
|
71
|
+
if (BASE_TABLES.EVENT === configInstance.tableNameEventQueue) {
|
|
72
|
+
cds.db.model.definitions[BASE_TABLES.EVENT] =
|
|
73
|
+
cds.model.definitions[BASE_TABLES.EVENT];
|
|
74
|
+
}
|
|
75
|
+
if (BASE_TABLES.LOCK === configInstance.tableNameEventLock) {
|
|
76
|
+
cds.db.model.definitions[BASE_TABLES.LOCK] =
|
|
77
|
+
cds.model.definitions[BASE_TABLES.LOCK];
|
|
78
|
+
}
|
|
79
|
+
dbHandler.registerEventQueueDbHandler(dbService);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
registerEventProcessors();
|
|
83
|
+
logger.info("event queue initialized", {
|
|
84
|
+
registerAsEventProcessor: configInstance.registerAsEventProcessor,
|
|
85
|
+
multiTenancyEnabled: configInstance.isMultiTenancy,
|
|
86
|
+
redisEnabled: configInstance.redisEnabled,
|
|
87
|
+
runInterval: configInstance.runInterval,
|
|
88
|
+
parallelTenantProcessing: configInstance.parallelTenantProcessing,
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const readConfigFromFile = async (configFilepath) => {
|
|
93
|
+
const fileData = await readFileAsync(configFilepath);
|
|
94
|
+
if (/\.ya?ml$/i.test(configFilepath)) {
|
|
95
|
+
return yaml.parse(fileData.toString());
|
|
96
|
+
}
|
|
97
|
+
if (/\.json$/i.test(configFilepath)) {
|
|
98
|
+
return JSON.parse(fileData.toString());
|
|
99
|
+
}
|
|
100
|
+
throw new VError(
|
|
101
|
+
{
|
|
102
|
+
name: VERROR_CLUSTER_NAME,
|
|
103
|
+
info: { configFilepath },
|
|
104
|
+
},
|
|
105
|
+
"configFilepath with unsupported extension, allowed extensions are .yaml and .json"
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const registerEventProcessors = () => {
|
|
110
|
+
const configInstance = getConfigInstance();
|
|
111
|
+
|
|
112
|
+
if (!configInstance.registerAsEventProcessor) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!configInstance.isMultiTenancy) {
|
|
117
|
+
runner.singleTenant();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (configInstance.redisEnabled) {
|
|
122
|
+
initEventQueueRedisSubscribe();
|
|
123
|
+
runner.multiTenancyRedis();
|
|
124
|
+
} else {
|
|
125
|
+
runner.multiTenancyDb();
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const csnCheck = async () => {
|
|
130
|
+
const configInstance = getConfigInstance();
|
|
131
|
+
const eventCsn = cds.model.definitions[configInstance.tableNameEventQueue];
|
|
132
|
+
if (!eventCsn) {
|
|
133
|
+
throw EventQueueError.missingTableInCsn(configInstance.tableNameEventQueue);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const lockCsn = cds.model.definitions[configInstance.tableNameEventLock];
|
|
137
|
+
if (!lockCsn) {
|
|
138
|
+
throw EventQueueError.missingTableInCsn(configInstance.tableNameEventLock);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (
|
|
142
|
+
configInstance.tableNameEventQueue === BASE_TABLES.EVENT &&
|
|
143
|
+
configInstance.tableNameEventLock === BASE_TABLES.LOCK
|
|
144
|
+
) {
|
|
145
|
+
return; // no need to check base tables
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const csn = await cds.load(path.join(__dirname, "..", "db"));
|
|
149
|
+
const baseEvent = csn.definitions["sap.eventqueue.Event"];
|
|
150
|
+
const baseLock = csn.definitions["sap.eventqueue.Lock"];
|
|
151
|
+
|
|
152
|
+
checkCustomTable(baseEvent, eventCsn);
|
|
153
|
+
checkCustomTable(baseLock, lockCsn);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const checkCustomTable = (baseCsn, customCsn) => {
|
|
157
|
+
for (const columnName in baseCsn.elements) {
|
|
158
|
+
if (!customCsn.elements[columnName]) {
|
|
159
|
+
throw EventQueueError.missingElementInTable(customCsn.name, columnName);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (
|
|
163
|
+
customCsn.elements[columnName].type !== "cds.Association" &&
|
|
164
|
+
customCsn.elements[columnName].type !==
|
|
165
|
+
baseCsn.elements[columnName].type &&
|
|
166
|
+
columnName === "status" &&
|
|
167
|
+
customCsn.elements[columnName].type !== "cds.Integer"
|
|
168
|
+
) {
|
|
169
|
+
throw EventQueueError.typeMismatchInTable(customCsn.name, columnName);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const mixConfigVarsWithEnv = (
|
|
175
|
+
configFilePath,
|
|
176
|
+
registerAsEventProcessor,
|
|
177
|
+
processEventsAfterPublish,
|
|
178
|
+
runInterval,
|
|
179
|
+
parallelTenantProcessing,
|
|
180
|
+
tableNameEventQueue,
|
|
181
|
+
tableNameEventLock,
|
|
182
|
+
skipCsnCheck
|
|
183
|
+
) => {
|
|
184
|
+
const configInstance = getConfigInstance();
|
|
185
|
+
|
|
186
|
+
configInstance.configFilePath =
|
|
187
|
+
configFilePath ?? cds.env.eventQueue?.configFilePath;
|
|
188
|
+
configInstance.registerAsEventProcessor =
|
|
189
|
+
registerAsEventProcessor ??
|
|
190
|
+
cds.env.eventQueue?.registerAsEventProcessor ??
|
|
191
|
+
true;
|
|
192
|
+
configInstance.processEventsAfterPublish =
|
|
193
|
+
processEventsAfterPublish ??
|
|
194
|
+
cds.env.eventQueue?.processEventsAfterPublish ??
|
|
195
|
+
true;
|
|
196
|
+
configInstance.runInterval =
|
|
197
|
+
runInterval ?? cds.env.eventQueue?.runInterval ?? 5 * 60 * 1000;
|
|
198
|
+
configInstance.parallelTenantProcessing =
|
|
199
|
+
parallelTenantProcessing ??
|
|
200
|
+
cds.env.eventQueue?.parallelTenantProcessing ??
|
|
201
|
+
5;
|
|
202
|
+
configInstance.tableNameEventQueue =
|
|
203
|
+
tableNameEventQueue ??
|
|
204
|
+
cds.env.eventQueue?.tableNameEventQueue ??
|
|
205
|
+
BASE_TABLES.EVENT;
|
|
206
|
+
configInstance.tableNameEventLock =
|
|
207
|
+
tableNameEventLock ??
|
|
208
|
+
cds.env.eventQueue?.tableNameEventLock ??
|
|
209
|
+
BASE_TABLES.LOCK;
|
|
210
|
+
configInstance.skipCsnCheck =
|
|
211
|
+
skipCsnCheck ?? cds.env.eventQueue?.skipCsnCheck ?? false;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
module.exports = {
|
|
215
|
+
initialize,
|
|
216
|
+
};
|