Skip to content

Commit 07a81d2

Browse files
committed
CDS 8 adjustments
1 parent 0d820c8 commit 07a81d2

File tree

13 files changed

+467
-1392
lines changed

13 files changed

+467
-1392
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8-
## Version 1.1.0 - 2024-07-xx
8+
## Version 1.1.0 - 2024-07-16
99

1010
### Added
1111

package-lock.json

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

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,21 @@
5050
},
5151
"devDependencies": {
5252
"@cap-js-community/websocket": "./",
53-
"@cap-js/sqlite": "^1.7.2",
54-
"@eslint/js": "^9.6.0",
55-
"@sap/cds": "^7.9.3",
56-
"@sap/cds-dk": "^7.9.4",
53+
"@cap-js/sqlite": "^1.7.3",
54+
"@eslint/js": "^9.7.0",
55+
"@sap/cds": "^8.0.3",
56+
"@sap/cds-dk": "^8.0.2",
5757
"@socket.io/redis-adapter": "^8.3.0",
5858
"@socket.io/redis-streams-adapter": "^0.2.2",
5959
"@types/express": "^4.17.21",
60-
"eslint": "^9.6.0",
60+
"eslint": "^9.7.0",
6161
"eslint-config-prettier": "^9.1.0",
6262
"eslint-plugin-jest": "^28.6.0",
6363
"eslint-plugin-n": "^17.9.0",
6464
"globals": "^15.8.0",
6565
"jest": "^29.7.0",
6666
"passport": "^0.7.0",
67-
"prettier": "^3.3.2",
67+
"prettier": "^3.3.3",
6868
"socket.io-client": "^4.7.5"
6969
},
7070
"license": "Apache-2.0",

src/socket/base.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class SocketServer {
186186
* @returns {[function]} Returns a list of middleware functions
187187
*/
188188
afterMiddlewares() {
189-
return [];
189+
return [this.removeWrapNext.bind(this)];
190190
}
191191

192192
/**
@@ -196,7 +196,16 @@ class SocketServer {
196196
middlewares() {
197197
function wrapMiddleware(middleware) {
198198
return (socket, next) => {
199-
return middleware(socket.request, socket.request.res, next);
199+
let nextCalled = false;
200+
const wrapNext = (err) => {
201+
delete socket.request._next;
202+
if (!nextCalled) {
203+
nextCalled = true;
204+
next(err);
205+
}
206+
};
207+
socket.request._next = wrapNext;
208+
return middleware(socket.request, socket.request.res, wrapNext);
200209
};
201210
}
202211

@@ -213,14 +222,26 @@ class SocketServer {
213222
return middlewares.concat(this.afterMiddlewares());
214223
}
215224

225+
removeWrapNext(socket, next) {
226+
const req = socket.request;
227+
let error;
228+
try {
229+
delete req._next;
230+
} catch (err) {
231+
error = err;
232+
} finally {
233+
next(error);
234+
}
235+
}
236+
216237
/**
217238
* Mock the HTTP response object and make available at req.res
218239
* @param {Object} socket Server socket
219240
* @param {function} next Call next
220241
*/
221242
mockResponse(socket, next) {
222243
const req = socket.request;
223-
let error = null;
244+
let error;
224245
try {
225246
// Mock response (not available in websocket, CDS middlewares need it)
226247
const res = req.res ?? {};
@@ -245,13 +266,17 @@ class SocketServer {
245266
return res;
246267
};
247268
res.json ??= (json) => {
248-
this.respond(socket, res.statusCode, JSON.stringify(json));
249-
res.body = json;
269+
res.send(JSON.stringify(json));
270+
return res;
271+
};
272+
res.sendStatus ??= (statusCode) => {
273+
res.status(statusCode);
274+
res.send(res.body || "" + statusCode);
250275
return res;
251276
};
252277
res.send ??= (text) => {
253-
this.respond(socket, res.statusCode, text);
254278
res.body = text;
279+
this.respond(socket, res.statusCode, text);
255280
return res;
256281
};
257282
res.end ??= () => {
@@ -275,7 +300,7 @@ class SocketServer {
275300
*/
276301
applyAuthCookie(socket, next) {
277302
const req = socket.request;
278-
let error = null;
303+
let error;
279304
try {
280305
// Apply cookie to authorization header
281306
if (["mocked"].includes(cds.env.requires?.auth?.kind) && !req.headers.authorization && req.headers.cookie) {

src/socket/socket.io.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,16 @@ class SocketIOServer extends SocketServer {
128128
to.emit(event, data);
129129
}
130130

131+
respond(socket, statusCode, body) {
132+
super.respond(socket, statusCode, body);
133+
if (statusCode >= 400 && socket.request?._next) {
134+
socket.request?._next(new Error(body));
135+
}
136+
}
137+
131138
close(socket) {
132139
if (socket) {
133-
socket.disconnect();
140+
socket.disconnect(true);
134141
} else {
135142
this.io.close();
136143
}

test/_env/util/socket.io.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const auth = require("./auth");
77

88
async function connect(service, options = {}) {
99
const port = cds.app.server.address().port;
10-
const socket = ioc(`http://localhost:${port}/${service}${options?.id ? "?id=options?.id" : ""}`, {
10+
const socket = ioc(`http://localhost:${port}/${service}${options?.id ? `?id=${options?.id}` : ""}`, {
1111
path: "/ws",
1212
extraHeaders: {
1313
authorization: options?.authorization || auth.alice,
@@ -20,7 +20,9 @@ async function connect(service, options = {}) {
2020
socket.once("connect", () => {
2121
resolve(socket);
2222
});
23-
socket.once("connect_error", reject);
23+
socket.once("connect_error", (err) => {
24+
reject(err);
25+
});
2426
});
2527
}
2628

@@ -51,10 +53,10 @@ async function waitForNoEvent(socket, event, timeout = 100) {
5153
return new Promise((resolve, reject) => {
5254
const timeoutId = setTimeout(() => {
5355
resolve();
54-
}, timeout);
56+
}, timeout).unref();
5557
socket.once(event, (result) => {
5658
clearTimeout(timeoutId);
57-
reject(result);
59+
reject(new Error(JSON.stringify(result)));
5860
});
5961
});
6062
}

test/_env/util/ws.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ async function waitForNoEvent(socket, event, timeout = 100) {
6161
return new Promise((resolve, reject) => {
6262
const timeoutId = setTimeout(() => {
6363
resolve();
64-
}, timeout);
64+
}, timeout).unref();
6565
socket._listeners.push((message) => {
6666
const payload = JSON.parse(message);
6767
if (payload.event === event) {
6868
clearTimeout(timeoutId);
69-
reject(payload.data);
69+
reject(new Error(JSON.stringify(payload.data)));
7070
}
7171
});
7272
});

test/base/base.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ describe("Base", () => {
8585
"x-correlation-id": "123",
8686
});
8787
expect(req.res.json({ A: 1 })).toBe(req.res);
88-
expect(req.res.body).toEqual({ A: 1 });
88+
expect(req.res.body).toEqual('{"A":1}');
89+
expect(req.res.sendStatus(200)).toBe(req.res);
8990
expect(req.res.send("Good Day!")).toBe(req.res);
9091
expect(req.res.body).toEqual("Good Day!");
9192
expect(req.res.end()).toBe(req.res);

test/socketio/auth_socket.io.test.js

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const cds = require("@sap/cds");
44

5-
const { connect, disconnect, emitEvent } = require("../_env/util/socket.io");
5+
const { connect } = require("../_env/util/socket.io");
66
const auth = require("../_env/util/auth");
77

88
cds.test(__dirname + "/../_env");
@@ -13,28 +13,12 @@ cds.env.websocket = {
1313
};
1414

1515
describe("Auth", () => {
16-
let socket;
17-
18-
beforeAll(async () => {
19-
socket = await connect("chat", {
20-
authorization: auth.invalid,
21-
});
22-
});
23-
24-
afterAll(async () => {
25-
await disconnect(socket);
26-
});
27-
28-
// TODO: CDS basic-auth does not call next(err). Socket.IO client is not connected and promise is pending
29-
test.skip("Invalid Auth", async () => {
30-
await new Promise((resolve) => {
31-
socket.on("disconnect", (event) => {
32-
expect(event).toBeDefined();
33-
resolve();
34-
});
35-
});
36-
await emitEvent(socket, "message", { text: "test" });
37-
cds.ws.close(socket);
16+
test("Invalid Auth", async () => {
17+
await expect(
18+
connect("chat", {
19+
authorization: auth.invalid,
20+
}),
21+
).rejects.toThrow(new Error("401"));
3822
cds.ws.close();
3923
});
4024
});

test/socketio/identifier_socket.io.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ describe("OData", () => {
2525

2626
afterAll(async () => {
2727
await disconnect(socket);
28+
await disconnect(socketOther);
2829
});
2930

3031
test("Identifier Event", async () => {

0 commit comments

Comments
 (0)