Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cf59b2d

Browse files
authored
regex as app name (#324)
Co-authored-by: Max Gruenfelder <[email protected]>
1 parent 2f5899b commit cf59b2d

File tree

5 files changed

+169
-6
lines changed

5 files changed

+169
-6
lines changed

src/config.js

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,50 @@ class Config {
153153
return !!this.#env.redisRequires?.credentials;
154154
}
155155

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+
156170
shouldBeProcessedInThisApplication(type, subType) {
157171
const config = this.#eventMap[this.generateKey(type, subType)];
158172
const appNameConfig = config._appNameMap;
159173
const appInstanceConfig = config._appInstancesMap;
174+
let result = true;
160175
if (!appNameConfig && !appInstanceConfig) {
161-
return true;
176+
return result;
162177
}
163178

164179
if (appNameConfig) {
165-
const shouldBeProcessedBasedOnAppName = appNameConfig[this.#env.applicationName];
166-
if (!shouldBeProcessedBasedOnAppName) {
167-
return false;
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+
}
168200
}
169201
}
170202

@@ -175,7 +207,7 @@ class Config {
175207
}
176208
}
177209

178-
return true;
210+
return result;
179211
}
180212

181213
checkRedisEnabled() {
@@ -530,7 +562,12 @@ class Config {
530562
}
531563

532564
#basicEventTransformationAfterValidate(event) {
533-
event._appNameMap = event.appNames ? Object.fromEntries(new Map(event.appNames.map((a) => [a, true]))) : null;
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;
534571
event._appInstancesMap = event.appInstances
535572
? Object.fromEntries(new Map(event.appInstances.map((a) => [a, true])))
536573
: null;

test/__snapshots__/eventQueueOutbox.test.js.snap

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true ad-hoc events overwrite se
2727
exports[`event-queue outbox monkeyPatchCAPOutbox=true ad-hoc events overwrite settings via outbox.events specific ad-hoc event should create own config 1`] = `
2828
{
2929
"_appInstancesMap": null,
30+
"_appNameContainsRegex": null,
3031
"_appNameMap": null,
3132
"checkForNextChunk": false,
3233
"impl": "./outbox/EventQueueGenericOutboxHandler",
@@ -50,6 +51,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true ad-hoc events overwrite se
5051
exports[`event-queue outbox monkeyPatchCAPOutbox=true custom options should win over service options 1`] = `
5152
{
5253
"_appInstancesMap": null,
54+
"_appNameContainsRegex": null,
5355
"_appNameMap": null,
5456
"checkForNextChunk": true,
5557
"impl": "./outbox/EventQueueGenericOutboxHandler",
@@ -85,6 +87,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true error in srv.after 1`] = `
8587
exports[`event-queue outbox monkeyPatchCAPOutbox=true map config to event-queue config 1`] = `
8688
{
8789
"_appInstancesMap": null,
90+
"_appNameContainsRegex": null,
8891
"_appNameMap": null,
8992
"checkForNextChunk": true,
9093
"impl": "./outbox/EventQueueGenericOutboxHandler",
@@ -119,6 +122,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true option to use eventQueue.u
119122
exports[`event-queue outbox monkeyPatchCAPOutbox=true periodic events inherit config cron/interval on top level is not allowed 1`] = `
120123
{
121124
"_appInstancesMap": null,
125+
"_appNameContainsRegex": null,
122126
"_appNameMap": null,
123127
"checkForNextChunk": true,
124128
"cron": "*/15 * * * * *",
@@ -151,6 +155,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true periodic events inherit co
151155
exports[`event-queue outbox monkeyPatchCAPOutbox=true periodic events inherit config overwrite in specific section 1`] = `
152156
{
153157
"_appInstancesMap": null,
158+
"_appNameContainsRegex": null,
154159
"_appNameMap": null,
155160
"checkForNextChunk": true,
156161
"cron": "*/15 * * * * *",
@@ -174,6 +179,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true periodic events inherit co
174179
exports[`event-queue outbox monkeyPatchCAPOutbox=true periodic events inherit config simple push down 1`] = `
175180
{
176181
"_appInstancesMap": null,
182+
"_appNameContainsRegex": null,
177183
"_appNameMap": null,
178184
"checkForNextChunk": true,
179185
"cron": "*/15 * * * * *",
@@ -344,6 +350,7 @@ exports[`event-queue outbox monkeyPatchCAPOutbox=true should store correct user
344350
exports[`event-queue outbox monkeyPatchCAPOutbox=true should work for outboxed services by require with transactionMode config 1`] = `
345351
{
346352
"_appInstancesMap": null,
353+
"_appNameContainsRegex": null,
347354
"_appNameMap": null,
348355
"checkForNextChunk": true,
349356
"impl": "./outbox/EventQueueGenericOutboxHandler",

test/__snapshots__/initialize.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ exports[`initialize read yaml config file 1`] = `
44
[
55
{
66
"_appInstancesMap": null,
7+
"_appNameContainsRegex": null,
78
"_appNameMap": null,
89
"checkForNextChunk": true,
910
"impl": "./srv/notification/EventQueueNotificationProcessor",
@@ -19,6 +20,7 @@ exports[`initialize read yaml config file 1`] = `
1920
},
2021
{
2122
"_appInstancesMap": null,
23+
"_appNameContainsRegex": null,
2224
"_appNameMap": null,
2325
"checkForNextChunk": true,
2426
"impl": "./srv/businessLogs/EventQueueBusinessLogProcessor",
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use strict";
2+
3+
const cds = require("@sap/cds");
4+
5+
const ACTION = ["appNamesRegex", "appNamesString", "appNamesMixStringMatch"];
6+
7+
class ServiceAppNames extends cds.Service {
8+
async init() {
9+
await super.init();
10+
11+
for (const actionName of ACTION) {
12+
this.on(actionName, (req) => {
13+
cds.log(this.name).info(req.event, {
14+
data: req.data,
15+
user: req.user.id,
16+
subType: req.eventQueue.processor.eventSubType,
17+
});
18+
});
19+
}
20+
}
21+
}
22+
23+
module.exports = ServiceAppNames;

test/eventQueueOutbox.test.js

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const { EventProcessingStatus } = require("../src/constants");
1717
const { checkAndInsertPeriodicEvents } = require("../src/periodicEvents");
1818
const { getOpenQueueEntries } = require("../src/runner/openEvents");
1919
const EventQueueGenericOutboxHandler = require("../src/outbox/EventQueueGenericOutboxHandler");
20+
const { getEnvInstance } = require("../src/shared/env");
2021

2122
const CUSTOM_HOOKS_SRV = "OutboxCustomHooks";
2223

@@ -56,6 +57,28 @@ cds.env.requires.OutboxCustomHooks = {
5657
},
5758
};
5859

60+
cds.env.requires.AppNames = {
61+
impl: "./outboxProject/srv/service/serviceAppNames.js",
62+
outbox: {
63+
kind: "persistent-outbox",
64+
checkForNextChunk: false,
65+
events: {
66+
appNamesString: {
67+
appNames: [],
68+
},
69+
appNamesRegex: {
70+
appNames: ["/srv-backend.*/i"],
71+
},
72+
appNamesMixStringMatch: {
73+
appNames: ["/srv-backend.*/i", "a-srv-backend"],
74+
},
75+
appNamesMixRegexMatch: {
76+
appNames: ["/a-srv-backend.*/i", "srv-backend"],
77+
},
78+
},
79+
},
80+
};
81+
5982
cds.env.requires.StandardService = {
6083
impl: "./outboxProject/srv/service/standard-service.js",
6184
outbox: {
@@ -1401,6 +1424,77 @@ describe("event-queue outbox", () => {
14011424
});
14021425
});
14031426
});
1427+
1428+
describe("app names", () => {
1429+
let env = getEnvInstance();
1430+
beforeEach(() => {
1431+
env.vcapApplication = {};
1432+
});
1433+
1434+
it("regex - no match", async () => {
1435+
const service = (await cds.connect.to("AppNames")).tx(context);
1436+
const data = { to: "to" };
1437+
await service.send("appNamesRegex", data);
1438+
await commitAndOpenNew();
1439+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1440+
await processEventQueue(tx.context, "CAP_OUTBOX", "AppNames.appNamesRegex");
1441+
await commitAndOpenNew();
1442+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1443+
expect(loggerMock.callsLengths().error).toEqual(0);
1444+
});
1445+
1446+
it("regex - match", async () => {
1447+
const service = (await cds.connect.to("AppNames")).tx(context);
1448+
env.vcapApplication = { application_name: `srv-backend-${cds.utils.uuid()}` };
1449+
const data = { to: "to" };
1450+
await service.send("appNamesRegex", data);
1451+
await commitAndOpenNew();
1452+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1453+
await processEventQueue(tx.context, "CAP_OUTBOX", "AppNames.appNamesRegex");
1454+
await commitAndOpenNew();
1455+
await testHelper.selectEventQueueAndExpectDone(tx, { expectedLength: 1 });
1456+
expect(loggerMock.callsLengths().error).toEqual(0);
1457+
});
1458+
1459+
it("mix - regex no match - string match", async () => {
1460+
const service = (await cds.connect.to("AppNames")).tx(context);
1461+
env.vcapApplication = { application_name: `a-srv-backend` };
1462+
const data = { to: "to" };
1463+
await service.send("appNamesMixStringMatch", data);
1464+
await commitAndOpenNew();
1465+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1466+
await processEventQueue(tx.context, "CAP_OUTBOX", "AppNames.appNamesMixStringMatch");
1467+
await commitAndOpenNew();
1468+
await testHelper.selectEventQueueAndExpectDone(tx, { expectedLength: 1 });
1469+
expect(loggerMock.callsLengths().error).toEqual(0);
1470+
});
1471+
1472+
it("mix - regex match - string no match", async () => {
1473+
const service = (await cds.connect.to("AppNames")).tx(context);
1474+
env.vcapApplication = { application_name: `a-srv-backend-${cds.utils.uuid()}` };
1475+
const data = { to: "to" };
1476+
await service.send("appNamesMixRegexMatch", data);
1477+
await commitAndOpenNew();
1478+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1479+
await processEventQueue(tx.context, "CAP_OUTBOX", "AppNames.appNamesMixRegexMatch");
1480+
await commitAndOpenNew();
1481+
await testHelper.selectEventQueueAndExpectDone(tx, { expectedLength: 1 });
1482+
expect(loggerMock.callsLengths().error).toEqual(0);
1483+
});
1484+
1485+
it("mix - regex no match - string no match", async () => {
1486+
const service = (await cds.connect.to("AppNames")).tx(context);
1487+
env.vcapApplication = { application_name: `srv--backend` };
1488+
const data = { to: "to" };
1489+
await service.send("appNamesMixStringMatch", data);
1490+
await commitAndOpenNew();
1491+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1492+
await processEventQueue(tx.context, "CAP_OUTBOX", "AppNames.appNamesMixStringMatch");
1493+
await commitAndOpenNew();
1494+
await testHelper.selectEventQueueAndExpectOpen(tx, { expectedLength: 1 });
1495+
expect(loggerMock.callsLengths().error).toEqual(0);
1496+
});
1497+
});
14041498
});
14051499

14061500
const commitAndOpenNew = async () => {

0 commit comments

Comments
 (0)