Skip to content

Commit 7495ab2

Browse files
committed
Adapter
1 parent aa93582 commit 7495ab2

File tree

7 files changed

+89
-110
lines changed

7 files changed

+89
-110
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1010
### Fixed
1111

1212
- Fix side effect event name and source properties
13-
- Queue/outbox websocket services automatically when configured accordingly in `cds.requires`
13+
- Prevent double websocket event emit with active adapter
1414
- Document how to handle websocket paths in different scenarios
1515
- Respect annotation `@Common.WebSocketChannel` for channel specification on event and service level
1616
- Document OData extension for UI5 Fiori Elements event-driven side effects
17+
- Queue/outbox websocket services automatically when configured accordingly in `cds.requires`
1718

1819
### Added
1920

package-lock.json

Lines changed: 17 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@
5151
"devDependencies": {
5252
"@cap-js-community/websocket": "./",
5353
"@cap-js/cds-test": "^0.4.1",
54-
"@cap-js/sqlite": "^2.0.4",
54+
"@cap-js/sqlite": "^2.1.0",
5555
"@eslint/js": "^9.39.1",
56-
"@sap/cds": "^9.4.4",
56+
"@sap/cds": "^9.4.5",
5757
"@sap/cds-dk": "^9.4.3",
5858
"@socket.io/redis-adapter": "^8.3.0",
5959
"@socket.io/redis-streams-adapter": "^0.2.3",
@@ -64,7 +64,7 @@
6464
"express": "^4.21.2",
6565
"globals": "^16.5.0",
6666
"jest": "^30.2.0",
67-
"prettier": "^3.6.2"
67+
"prettier": "^3.7.3"
6868
},
6969
"license": "Apache-2.0",
7070
"repository": {

src/socket/base.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class SocketServer {
181181
* @param {String} tenant Tenant for isolation
182182
* @param {String} service Service definition
183183
* @param {String} [path] Service path, e.g. "/path" (relative to websocket server path), undefined: default service path
184-
* @param {String} event Event name or event message JSON content (no additional parameters provided (incl. 'data', except 'local'))
184+
* @param {String} event Event name or event message JSON content (no additional parameters below allowed except 'local')
185185
* @param {Object} [data] Event data, undefined: event contains all info incl. data
186186
* @param {Object} [headers] Event headers
187187
* @param {Object} [filter] Filters

src/socket/ws.js

Lines changed: 65 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -186,74 +186,75 @@ class SocketWSServer extends SocketServer {
186186
context = message.context;
187187
identifier = message.identifier;
188188
}
189-
tenant = tenant || socket?.context.tenant;
190-
path = path || this.defaultPath(service);
191-
const serviceClients = this.fetchClients(tenant, this.servicePath(path));
192-
let clients = new Set(serviceClients.all);
193-
if (user?.include?.length || role?.include?.length || context?.include?.length || identifier?.include?.length) {
194-
switch (this.serviceOperator(service, event, "include")) {
195-
case "or":
196-
default:
197-
clients = new Set([
198-
...this.collectFromMap(serviceClients.users, user?.include),
199-
...this.collectFromMap(serviceClients.roles, role?.include),
200-
...this.collectFromMap(serviceClients.contexts, context?.include),
201-
...this.collectFromMap(serviceClients.identifiers, identifier?.include),
202-
]);
203-
break;
204-
case "and":
205-
user?.include?.length &&
206-
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.users, user.include));
207-
role?.include?.length &&
208-
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.roles, role.include));
209-
context?.include?.length &&
210-
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.contexts, context.include));
211-
identifier?.include?.length &&
212-
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.identifiers, identifier.include));
213-
break;
189+
if (!this.adapterActive || local) {
190+
tenant = tenant || socket?.context.tenant;
191+
path = path || this.defaultPath(service);
192+
const serviceClients = this.fetchClients(tenant, this.servicePath(path));
193+
let clients = new Set(serviceClients.all);
194+
if (user?.include?.length || role?.include?.length || context?.include?.length || identifier?.include?.length) {
195+
switch (this.serviceOperator(service, event, "include")) {
196+
case "or":
197+
default:
198+
clients = new Set([
199+
...this.collectFromMap(serviceClients.users, user?.include),
200+
...this.collectFromMap(serviceClients.roles, role?.include),
201+
...this.collectFromMap(serviceClients.contexts, context?.include),
202+
...this.collectFromMap(serviceClients.identifiers, identifier?.include),
203+
]);
204+
break;
205+
case "and":
206+
user?.include?.length &&
207+
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.users, user.include));
208+
role?.include?.length &&
209+
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.roles, role.include));
210+
context?.include?.length &&
211+
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.contexts, context.include));
212+
identifier?.include?.length &&
213+
this.keepEntriesFromSet(clients, this.collectFromMap(serviceClients.identifiers, identifier.include));
214+
break;
215+
}
214216
}
215-
}
216-
if (user?.exclude?.length || role?.exclude?.length || context?.exclude?.length || identifier?.exclude?.length) {
217-
switch (this.serviceOperator(service, event, "exclude")) {
218-
case "or":
219-
default:
220-
this.keepEntriesFromSet(
221-
clients,
222-
this.collectFromSet(clients, (client) => {
223-
return !(
224-
user?.exclude?.includes(client.context.user?.id) ||
225-
role?.exclude?.find((role) => client.context.user?.is(role)) ||
226-
context?.exclude?.find((context) => client.contexts.has(context)) ||
227-
identifier?.exclude?.includes(client.request?.id)
228-
);
229-
}),
230-
);
231-
break;
232-
case "and":
233-
this.keepEntriesFromSet(
234-
clients,
235-
this.collectFromSet(clients, (client) => {
236-
return !(
237-
(!user?.exclude?.length || user?.exclude.includes(client.context.user?.id)) &&
238-
(!role?.exclude?.length || role?.exclude.find((role) => client.context.user?.is(role))) &&
239-
(!context?.exclude?.length || context?.exclude.find((context) => client.contexts.has(context))) &&
240-
(!identifier?.exclude?.length || identifier?.exclude.includes(client.request?.id))
241-
);
242-
}),
243-
);
244-
break;
217+
if (user?.exclude?.length || role?.exclude?.length || context?.exclude?.length || identifier?.exclude?.length) {
218+
switch (this.serviceOperator(service, event, "exclude")) {
219+
case "or":
220+
default:
221+
this.keepEntriesFromSet(
222+
clients,
223+
this.collectFromSet(clients, (client) => {
224+
return !(
225+
user?.exclude?.includes(client.context.user?.id) ||
226+
role?.exclude?.find((role) => client.context.user?.is(role)) ||
227+
context?.exclude?.find((context) => client.contexts.has(context)) ||
228+
identifier?.exclude?.includes(client.request?.id)
229+
);
230+
}),
231+
);
232+
break;
233+
case "and":
234+
this.keepEntriesFromSet(
235+
clients,
236+
this.collectFromSet(clients, (client) => {
237+
return !(
238+
(!user?.exclude?.length || user?.exclude.includes(client.context.user?.id)) &&
239+
(!role?.exclude?.length || role?.exclude.find((role) => client.context.user?.is(role))) &&
240+
(!context?.exclude?.length || context?.exclude.find((context) => client.contexts.has(context))) &&
241+
(!identifier?.exclude?.length || identifier?.exclude.includes(client.request?.id))
242+
);
243+
}),
244+
);
245+
break;
246+
}
245247
}
246-
}
247-
if (clients.size > 0) {
248-
const format = this.format(service, event);
249-
const clientMessage = format.compose(event, data, headers);
250-
for (const client of clients) {
251-
if (client !== socket && client.readyState === WebSocket.OPEN) {
252-
await client.send(clientMessage);
248+
if (clients.size > 0) {
249+
const format = this.format(service, event);
250+
const clientMessage = format.compose(event, data, headers);
251+
for (const client of clients) {
252+
if (client !== socket && client.readyState === WebSocket.OPEN) {
253+
await client.send(clientMessage);
254+
}
253255
}
254256
}
255-
}
256-
if (!local) {
257+
} else {
257258
const adapterMessage = isEventMessage
258259
? eventMessage
259260
: JSON.stringify({ tenant, event, data, headers, user, role, context, identifier });

test/_env/srv/fiori.cds

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ service FioriService {
2323
action submitOrder(quantity : Books:stock @mandatory );
2424
};
2525

26-
@ws: { currentUser, format: 'pcp', pcp: { sideEffect } }
26+
@ws: { $value, currentUser, format: 'pcp', pcp: { sideEffect } }
2727
event stockChanged {
2828
sideEffectSource : String;
2929
};

0 commit comments

Comments
 (0)