systemlynx 1.18.12 → 1.20.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/API.md +700 -118
- package/README.md +106 -3
- package/RFCs/001-websocket-named-events-room-scoping.md +133 -0
- package/index.test.js +4 -0
- package/package.json +1 -1
- package/systemlynx/App/tests/App.test.js +73 -56
- package/systemlynx/Client/components/ServiceRequestHandler.js +43 -12
- package/systemlynx/Client/components/SocketDispatcher.js +80 -1
- package/systemlynx/Client/tests/Client.test.js +26 -0
- package/systemlynx/Client/tests/SocketDispatcher.test.js +12 -6
- package/systemlynx/Dispatcher/Dispatcher.js +71 -27
- package/systemlynx/Dispatcher/Dispatcher.test.js +87 -4
- package/systemlynx/LoadBalancer/tests/LoadBalancer.test.js +4 -0
- package/systemlynx/ServerManager/components/Router.js +12 -3
- package/systemlynx/ServerManager/components/SocketEmitter.js +7 -1
- package/systemlynx/ServerManager/tests/SocketEmitter.test.js +12 -9
- package/systemlynx/Service/Service.test.js +3 -1
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
const io = require("socket.io-client");
|
|
3
3
|
const createDispatcher = require("../../Dispatcher/Dispatcher");
|
|
4
4
|
|
|
5
|
+
const RESERVED = new Set(["connect", "disconnect", "error", "connect_error"]);
|
|
6
|
+
|
|
5
7
|
module.exports = function SocketDispatcher(
|
|
6
8
|
{ namespace, socketPath: path },
|
|
7
9
|
events = {},
|
|
@@ -13,8 +15,82 @@ module.exports = function SocketDispatcher(
|
|
|
13
15
|
: createDispatcher.apply(this, [events, systemContext]);
|
|
14
16
|
|
|
15
17
|
const socket = io.connect(namespace, { path });
|
|
18
|
+
const subscriptionCounts = new Map();
|
|
19
|
+
|
|
20
|
+
const trackSubscribe = (name) => {
|
|
21
|
+
const n = (subscriptionCounts.get(name) || 0) + 1;
|
|
22
|
+
subscriptionCounts.set(name, n);
|
|
23
|
+
if (n === 1) socket.emit("subscribe", name);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const trackUnsubscribe = (name) => {
|
|
27
|
+
const n = (subscriptionCounts.get(name) || 0) - 1;
|
|
28
|
+
if (n <= 0) {
|
|
29
|
+
subscriptionCounts.delete(name);
|
|
30
|
+
socket.emit("unsubscribe", name);
|
|
31
|
+
} else {
|
|
32
|
+
subscriptionCounts.set(name, n);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const originalOn = dispatcher.on.bind(dispatcher);
|
|
37
|
+
dispatcher.on = function (name, cb, options) {
|
|
38
|
+
const unsub = originalOn(name, cb, options);
|
|
39
|
+
if (!RESERVED.has(name)) {
|
|
40
|
+
trackSubscribe(name);
|
|
41
|
+
return function () {
|
|
42
|
+
unsub();
|
|
43
|
+
trackUnsubscribe(name);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return unsub;
|
|
47
|
+
};
|
|
16
48
|
|
|
17
|
-
|
|
49
|
+
const originalOnce = dispatcher.once.bind(dispatcher);
|
|
50
|
+
dispatcher.once = function (name, cb, options) {
|
|
51
|
+
if (RESERVED.has(name)) return originalOnce(name, cb, options);
|
|
52
|
+
let done = false;
|
|
53
|
+
trackSubscribe(name);
|
|
54
|
+
const unsub = originalOnce(
|
|
55
|
+
name,
|
|
56
|
+
function (...args) {
|
|
57
|
+
if (!done) {
|
|
58
|
+
done = true;
|
|
59
|
+
trackUnsubscribe(name);
|
|
60
|
+
cb(...args);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
options
|
|
64
|
+
);
|
|
65
|
+
return function () {
|
|
66
|
+
if (!done) {
|
|
67
|
+
done = true;
|
|
68
|
+
unsub();
|
|
69
|
+
trackUnsubscribe(name);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const originalClearEvent = dispatcher.$clearEvent.bind(dispatcher);
|
|
75
|
+
dispatcher.$clearEvent = function (name) {
|
|
76
|
+
originalClearEvent(name);
|
|
77
|
+
if (subscriptionCounts.has(name)) {
|
|
78
|
+
subscriptionCounts.delete(name);
|
|
79
|
+
socket.emit("unsubscribe", name);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const originalDestroy = dispatcher.destroy.bind(dispatcher);
|
|
84
|
+
dispatcher.destroy = function () {
|
|
85
|
+
subscriptionCounts.forEach((_, name) => socket.emit("unsubscribe", name));
|
|
86
|
+
subscriptionCounts.clear();
|
|
87
|
+
originalDestroy();
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
socket.onAny((name, payload) => {
|
|
91
|
+
const event = { id: payload.id, name, data: payload.data, type: payload.type };
|
|
92
|
+
dispatcher.emit(name, payload.data, event);
|
|
93
|
+
});
|
|
18
94
|
|
|
19
95
|
socket.on("disconnect", () => {
|
|
20
96
|
socket.disconnect();
|
|
@@ -22,6 +98,9 @@ module.exports = function SocketDispatcher(
|
|
|
22
98
|
});
|
|
23
99
|
|
|
24
100
|
socket.on("connect", () => {
|
|
101
|
+
subscriptionCounts.forEach((count, name) => {
|
|
102
|
+
if (count > 0) socket.emit("subscribe", name);
|
|
103
|
+
});
|
|
25
104
|
dispatcher.emit("connect");
|
|
26
105
|
});
|
|
27
106
|
|
|
@@ -46,7 +46,9 @@ describe("Client", () => {
|
|
|
46
46
|
.that.has.all.keys(
|
|
47
47
|
"emit",
|
|
48
48
|
"on",
|
|
49
|
+
"once",
|
|
49
50
|
"$clearEvent",
|
|
51
|
+
"destroy",
|
|
50
52
|
"resetConnection",
|
|
51
53
|
"disconnect",
|
|
52
54
|
"headers",
|
|
@@ -66,7 +68,9 @@ describe("Client", () => {
|
|
|
66
68
|
.that.has.all.keys(
|
|
67
69
|
"emit",
|
|
68
70
|
"on",
|
|
71
|
+
"once",
|
|
69
72
|
"$clearEvent",
|
|
73
|
+
"destroy",
|
|
70
74
|
"disconnect",
|
|
71
75
|
"headers",
|
|
72
76
|
"setHeaders",
|
|
@@ -292,6 +296,9 @@ describe("Service", () => {
|
|
|
292
296
|
this.save = ({ file, files, message }) => {
|
|
293
297
|
return { file, files, message };
|
|
294
298
|
};
|
|
299
|
+
this.testOtherParams = (param1, { file, files, message }) => {
|
|
300
|
+
return { files, message, param1 };
|
|
301
|
+
};
|
|
295
302
|
});
|
|
296
303
|
await service.startService({
|
|
297
304
|
route,
|
|
@@ -311,6 +318,10 @@ describe("Service", () => {
|
|
|
311
318
|
message: "multi file upload test confirmation",
|
|
312
319
|
});
|
|
313
320
|
|
|
321
|
+
const extraParamResponse = await storage.testOtherParams("OtherParamsTest", {
|
|
322
|
+
files: [TEST_FILE, fs.createReadStream(TEST_FILE)],
|
|
323
|
+
message: "other params file upload test confirmation",
|
|
324
|
+
});
|
|
314
325
|
expect(singleFileResponse).to.be.an("object").that.has.all.keys("file", "message");
|
|
315
326
|
expect(singleFileResponse.message).to.be.an("string");
|
|
316
327
|
expect(singleFileResponse.message).to.equal("single file upload test confirmation");
|
|
@@ -326,6 +337,21 @@ describe("Service", () => {
|
|
|
326
337
|
expect(multiFileResponse.files[1]).to.be.an("object");
|
|
327
338
|
expect(multiFileResponse.files[0].originalname).to.equal("test.file.json");
|
|
328
339
|
expect(multiFileResponse.files[1].mimetype).to.equal("application/json");
|
|
340
|
+
|
|
341
|
+
expect(extraParamResponse)
|
|
342
|
+
.to.be.an("object")
|
|
343
|
+
.that.has.all.keys("files", "message", "param1");
|
|
344
|
+
expect(extraParamResponse.message).to.be.an("string");
|
|
345
|
+
expect(extraParamResponse.message).to.equal(
|
|
346
|
+
"other params file upload test confirmation"
|
|
347
|
+
);
|
|
348
|
+
expect(extraParamResponse.param1).to.be.an("string");
|
|
349
|
+
expect(extraParamResponse.param1).to.equal("OtherParamsTest");
|
|
350
|
+
expect(extraParamResponse.files).to.be.an("array");
|
|
351
|
+
expect(extraParamResponse.files[0]).to.be.an("object");
|
|
352
|
+
expect(extraParamResponse.files[1]).to.be.an("object");
|
|
353
|
+
expect(extraParamResponse.files[0].originalname).to.equal("test.file.json");
|
|
354
|
+
expect(extraParamResponse.files[1].mimetype).to.equal("application/json");
|
|
329
355
|
});
|
|
330
356
|
|
|
331
357
|
it("should maintain service and module level headers on a Client instance", async () => {
|
|
@@ -11,6 +11,10 @@ const socket = WebSocket.of(route);
|
|
|
11
11
|
socket.on("connect", ({ id }) => {
|
|
12
12
|
console.log(`socket connected with id:${id}`);
|
|
13
13
|
});
|
|
14
|
+
socket.on("connection", (clientSocket) => {
|
|
15
|
+
clientSocket.on("subscribe", (name) => clientSocket.join(name));
|
|
16
|
+
clientSocket.on("unsubscribe", (name) => clientSocket.leave(name));
|
|
17
|
+
});
|
|
14
18
|
SocketServer.listen(port);
|
|
15
19
|
|
|
16
20
|
describe("SocketDispatcher", () => {
|
|
@@ -19,7 +23,7 @@ describe("SocketDispatcher", () => {
|
|
|
19
23
|
it("should return an EventDispatcher object with methods on and emit", async () => {
|
|
20
24
|
expect(dispatcher)
|
|
21
25
|
.to.be.an("object")
|
|
22
|
-
.that.has.all.keys("on", "emit", "$clearEvent", "disconnect")
|
|
26
|
+
.that.has.all.keys("on", "once", "emit", "$clearEvent", "destroy", "disconnect")
|
|
23
27
|
.that.respondsTo("on")
|
|
24
28
|
.that.respondsTo("emit")
|
|
25
29
|
.that.respondsTo("$clearEvent")
|
|
@@ -32,7 +36,7 @@ describe("SocketDispatcher", () => {
|
|
|
32
36
|
});
|
|
33
37
|
dispatcher.on("connect", () => console.log(`I'm all the way connected!`));
|
|
34
38
|
setTimeout(
|
|
35
|
-
() => socket.emit(
|
|
39
|
+
() => socket.to(eventName).emit(eventName, { id: "test-id", data: { testPassed: true }, type: "WebSocket" }),
|
|
36
40
|
500
|
|
37
41
|
);
|
|
38
42
|
});
|
|
@@ -44,7 +48,7 @@ describe("SocketDispatcher.apply()", () => {
|
|
|
44
48
|
it("should return an EventDispatcher object with methods on and emit", async () => {
|
|
45
49
|
expect(dispatcher)
|
|
46
50
|
.to.be.an("object")
|
|
47
|
-
.that.has.all.keys("on", "emit", "$clearEvent", "disconnect")
|
|
51
|
+
.that.has.all.keys("on", "once", "emit", "$clearEvent", "destroy", "disconnect")
|
|
48
52
|
.that.respondsTo("on")
|
|
49
53
|
.that.respondsTo("emit")
|
|
50
54
|
.that.respondsTo("$clearEvent")
|
|
@@ -53,14 +57,16 @@ describe("SocketDispatcher.apply()", () => {
|
|
|
53
57
|
it("Should be able to emit and handle events", (done) => {
|
|
54
58
|
dispatcher.on(eventName, (data, event) => {
|
|
55
59
|
expect(data).to.deep.equal({ testPassed: true });
|
|
56
|
-
expect(event).to.
|
|
57
|
-
|
|
60
|
+
expect(event).to.be.an("object").that.has.all.keys("id", "name", "data", "type");
|
|
61
|
+
expect(event.name).to.equal(eventName);
|
|
62
|
+
expect(event.data).to.deep.equal({ testPassed: true });
|
|
63
|
+
expect(event.type).to.equal("WebSocket");
|
|
58
64
|
console.log(`I'm all the way connected too!`);
|
|
59
65
|
done();
|
|
60
66
|
});
|
|
61
67
|
|
|
62
68
|
setTimeout(
|
|
63
|
-
() => socket.emit(
|
|
69
|
+
() => socket.to(eventName).emit(eventName, { id: "test-id", data: { testPassed: true }, type: "WebSocket" }),
|
|
64
70
|
500
|
|
65
71
|
);
|
|
66
72
|
});
|
|
@@ -1,49 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
const throttle = require("../../utils/throttle");
|
|
2
3
|
|
|
3
|
-
module.exports = function createDispatcher(
|
|
4
|
+
module.exports = function createDispatcher(_, systemContext) {
|
|
5
|
+
const events = new Map();
|
|
4
6
|
const Dispatcher = this || {};
|
|
5
7
|
|
|
6
|
-
Dispatcher.emit = (eventName, data, event)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
);
|
|
8
|
+
Dispatcher.emit = function (eventName, data, event) {
|
|
9
|
+
const registry = events.get(eventName);
|
|
10
|
+
if (!registry) return Dispatcher;
|
|
11
|
+
for (const listener of registry.values()) {
|
|
12
|
+
listener(data, event);
|
|
13
|
+
}
|
|
11
14
|
return Dispatcher;
|
|
12
15
|
};
|
|
13
16
|
|
|
14
|
-
Dispatcher.on = (eventName, callback, { limit, interval } = {})
|
|
17
|
+
Dispatcher.on = function (eventName, callback, { limit, interval, eventId } = {}) {
|
|
15
18
|
if (typeof callback !== "function") return Dispatcher;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (!events
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return
|
|
19
|
+
|
|
20
|
+
const key = eventId || Symbol();
|
|
21
|
+
if (!events.has(eventName)) events.set(eventName, new Map());
|
|
22
|
+
const registry = events.get(eventName);
|
|
23
|
+
if (registry.has(key)) registry.delete(key);
|
|
24
|
+
|
|
25
|
+
let fn = typeof interval === "number" ? throttle(callback, limit, interval) : callback;
|
|
26
|
+
if (systemContext) fn = fn.bind(systemContext);
|
|
27
|
+
registry.set(key, fn);
|
|
28
|
+
|
|
29
|
+
return function () {
|
|
30
|
+
const currentRegistry = events.get(eventName);
|
|
31
|
+
if (!currentRegistry) return;
|
|
32
|
+
currentRegistry.delete(key);
|
|
33
|
+
if (currentRegistry.size === 0) events.delete(eventName);
|
|
34
|
+
};
|
|
27
35
|
};
|
|
28
36
|
|
|
29
|
-
Dispatcher
|
|
30
|
-
if (
|
|
37
|
+
Dispatcher.once = function (eventName, callback, { limit, interval, eventId } = {}) {
|
|
38
|
+
if (typeof callback !== "function") return function () {};
|
|
39
|
+
|
|
40
|
+
const key = eventId || Symbol();
|
|
41
|
+
if (!events.has(eventName)) events.set(eventName, new Map());
|
|
42
|
+
const registry = events.get(eventName);
|
|
43
|
+
if (registry.has(key)) registry.delete(key);
|
|
44
|
+
|
|
45
|
+
const throttled =
|
|
46
|
+
typeof interval === "number" ? throttle(callback, limit, interval) : callback;
|
|
47
|
+
|
|
48
|
+
const boundFn = function (...args) {
|
|
49
|
+
registry.delete(key);
|
|
50
|
+
if (registry.size === 0) events.delete(eventName);
|
|
51
|
+
return throttled.apply(systemContext, args);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
registry.set(key, boundFn);
|
|
55
|
+
|
|
56
|
+
return function () {
|
|
57
|
+
const currentRegistry = events.get(eventName);
|
|
58
|
+
if (!currentRegistry) return;
|
|
59
|
+
currentRegistry.delete(key);
|
|
60
|
+
if (currentRegistry.size === 0) events.delete(eventName);
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
Dispatcher.$clearEvent = function (eventName, fn) {
|
|
65
|
+
if (!events.get(eventName)) return Dispatcher;
|
|
31
66
|
|
|
32
67
|
if (!fn) {
|
|
33
|
-
|
|
34
|
-
delete events[eventName];
|
|
68
|
+
events.delete(eventName);
|
|
35
69
|
} else if (typeof fn === "function") {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
70
|
+
const registry = events.get(eventName);
|
|
71
|
+
for (const [key, listener] of registry.entries()) {
|
|
72
|
+
if (listener.name === fn.name) {
|
|
73
|
+
registry.delete(key);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (registry.size === 0) events.delete(eventName);
|
|
40
78
|
} else {
|
|
41
79
|
console.error(
|
|
42
|
-
"SystemLynxError: the second parameter of the Dispatcher.$clearEvent takes the original function
|
|
80
|
+
"SystemLynxError: the second parameter of the Dispatcher.$clearEvent takes the original function to the event"
|
|
43
81
|
);
|
|
44
82
|
}
|
|
45
83
|
|
|
46
84
|
return Dispatcher;
|
|
47
85
|
};
|
|
86
|
+
|
|
87
|
+
Dispatcher.destroy = function () {
|
|
88
|
+
events.clear();
|
|
89
|
+
return Dispatcher;
|
|
90
|
+
};
|
|
91
|
+
|
|
48
92
|
return Dispatcher;
|
|
49
93
|
};
|
|
@@ -3,15 +3,18 @@ const Dispatcher = require("./Dispatcher");
|
|
|
3
3
|
|
|
4
4
|
describe("createDispatcher", () => {
|
|
5
5
|
const dispatcher = new Dispatcher();
|
|
6
|
-
|
|
6
|
+
|
|
7
|
+
it("should return an EventDispatcher object with on, emit, $clearEvent, once, and destroy", () => {
|
|
7
8
|
expect(dispatcher)
|
|
8
9
|
.to.be.an("object")
|
|
9
|
-
.that.has.all.keys("on", "emit", "$clearEvent")
|
|
10
10
|
.that.respondsTo("on")
|
|
11
11
|
.that.respondsTo("emit")
|
|
12
|
-
.that.respondsTo("$clearEvent")
|
|
12
|
+
.that.respondsTo("$clearEvent")
|
|
13
|
+
.that.respondsTo("once")
|
|
14
|
+
.that.respondsTo("destroy");
|
|
13
15
|
});
|
|
14
|
-
|
|
16
|
+
|
|
17
|
+
it("should emit and handle events", (done) => {
|
|
15
18
|
dispatcher.on("test", (data) => {
|
|
16
19
|
expect(data).to.deep.equal({ testPassed: true });
|
|
17
20
|
done();
|
|
@@ -19,4 +22,84 @@ describe("createDispatcher", () => {
|
|
|
19
22
|
|
|
20
23
|
dispatcher.emit("test", { testPassed: true });
|
|
21
24
|
});
|
|
25
|
+
|
|
26
|
+
it("on() should return an unsubscribe function that removes the listener", (done) => {
|
|
27
|
+
const d = new Dispatcher();
|
|
28
|
+
let callCount = 0;
|
|
29
|
+
|
|
30
|
+
const unsubscribe = d.on("ping", () => callCount++);
|
|
31
|
+
expect(unsubscribe).to.be.a("function");
|
|
32
|
+
|
|
33
|
+
d.emit("ping");
|
|
34
|
+
unsubscribe();
|
|
35
|
+
d.emit("ping");
|
|
36
|
+
|
|
37
|
+
setTimeout(() => {
|
|
38
|
+
expect(callCount).to.equal(1);
|
|
39
|
+
done();
|
|
40
|
+
}, 0);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("eventId should replace the existing listener on re-register", (done) => {
|
|
44
|
+
const d = new Dispatcher();
|
|
45
|
+
let callCount = 0;
|
|
46
|
+
|
|
47
|
+
d.on("ping", () => callCount++, { eventId: "my-listener" });
|
|
48
|
+
d.on("ping", () => callCount++, { eventId: "my-listener" });
|
|
49
|
+
|
|
50
|
+
d.emit("ping");
|
|
51
|
+
|
|
52
|
+
setTimeout(() => {
|
|
53
|
+
expect(callCount).to.equal(1);
|
|
54
|
+
done();
|
|
55
|
+
}, 0);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("once() should fire the callback only once", (done) => {
|
|
59
|
+
const d = new Dispatcher();
|
|
60
|
+
let callCount = 0;
|
|
61
|
+
|
|
62
|
+
d.once("ping", () => callCount++);
|
|
63
|
+
d.emit("ping");
|
|
64
|
+
d.emit("ping");
|
|
65
|
+
d.emit("ping");
|
|
66
|
+
|
|
67
|
+
setTimeout(() => {
|
|
68
|
+
expect(callCount).to.equal(1);
|
|
69
|
+
done();
|
|
70
|
+
}, 0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("destroy() should remove all listeners", (done) => {
|
|
74
|
+
const d = new Dispatcher();
|
|
75
|
+
let callCount = 0;
|
|
76
|
+
|
|
77
|
+
d.on("a", () => callCount++);
|
|
78
|
+
d.on("b", () => callCount++);
|
|
79
|
+
d.destroy();
|
|
80
|
+
d.emit("a");
|
|
81
|
+
d.emit("b");
|
|
82
|
+
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
expect(callCount).to.equal(0);
|
|
85
|
+
done();
|
|
86
|
+
}, 0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("$clearEvent should still work with a named function (backward compat)", (done) => {
|
|
90
|
+
const d = new Dispatcher();
|
|
91
|
+
let callCount = 0;
|
|
92
|
+
|
|
93
|
+
function myHandler() { callCount++; }
|
|
94
|
+
|
|
95
|
+
d.on("ping", myHandler);
|
|
96
|
+
d.emit("ping");
|
|
97
|
+
d.$clearEvent("ping", myHandler);
|
|
98
|
+
d.emit("ping");
|
|
99
|
+
|
|
100
|
+
setTimeout(() => {
|
|
101
|
+
expect(callCount).to.equal(1);
|
|
102
|
+
done();
|
|
103
|
+
}, 0);
|
|
104
|
+
});
|
|
22
105
|
});
|
|
@@ -26,8 +26,10 @@ describe("LoadBalancer()", () => {
|
|
|
26
26
|
.to.be.an("object")
|
|
27
27
|
.that.has.all.keys(
|
|
28
28
|
"on",
|
|
29
|
+
"once",
|
|
29
30
|
"emit",
|
|
30
31
|
"$clearEvent",
|
|
32
|
+
"destroy",
|
|
31
33
|
"before",
|
|
32
34
|
"after",
|
|
33
35
|
"clones",
|
|
@@ -93,9 +95,11 @@ describe("LoadBalancer.clones (Module)", () => {
|
|
|
93
95
|
.to.be.an("Object")
|
|
94
96
|
.that.has.all.keys(
|
|
95
97
|
"on",
|
|
98
|
+
"once",
|
|
96
99
|
"emit",
|
|
97
100
|
"$emit",
|
|
98
101
|
"$clearEvent",
|
|
102
|
+
"destroy",
|
|
99
103
|
"before",
|
|
100
104
|
"after",
|
|
101
105
|
"register",
|
|
@@ -34,6 +34,7 @@ module.exports = function createRouter(server, config) {
|
|
|
34
34
|
[`/${route}`],
|
|
35
35
|
(req, res, next) => {
|
|
36
36
|
req.module_name = module_name;
|
|
37
|
+
req.moduleName = module_name;
|
|
37
38
|
req.fn = method;
|
|
38
39
|
req.Module = Module;
|
|
39
40
|
req.module = Module;
|
|
@@ -80,11 +81,19 @@ module.exports = function createRouter(server, config) {
|
|
|
80
81
|
const getArguments = () => {
|
|
81
82
|
const args = body.__arguments || [];
|
|
82
83
|
if (!isEmpty(query) && !args.length) args.push(query);
|
|
83
|
-
if (isObject(args[0]) && method === "POST")
|
|
84
|
-
args[0] = { ...args[0], ...(file && { file }), ...(files && { files }) };
|
|
85
84
|
return args;
|
|
86
85
|
};
|
|
87
|
-
|
|
86
|
+
|
|
87
|
+
const args = getArguments();
|
|
88
|
+
|
|
89
|
+
args.forEach((arg) => {
|
|
90
|
+
if (isObject(arg)) {
|
|
91
|
+
if (arg.file === "__file__") arg.file = file;
|
|
92
|
+
if (arg.files === "__files__") arg.files = files;
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
req.arguments = args;
|
|
88
97
|
req.presets = presets;
|
|
89
98
|
res.sendError = sendError;
|
|
90
99
|
res.sendResponse = sendResponse;
|
|
@@ -6,13 +6,19 @@ module.exports = function SocketEmitter(namespace, WebSocket) {
|
|
|
6
6
|
(this || {}).on && (this || {}).emit ? this : createDispatcher.apply(this);
|
|
7
7
|
|
|
8
8
|
const socket = WebSocket.of(`/${namespace}`);
|
|
9
|
+
|
|
10
|
+
socket.on("connection", (clientSocket) => {
|
|
11
|
+
clientSocket.on("subscribe", (name) => clientSocket.join(name));
|
|
12
|
+
clientSocket.on("unsubscribe", (name) => clientSocket.leave(name));
|
|
13
|
+
});
|
|
14
|
+
|
|
9
15
|
//use $emit to emit events locally only
|
|
10
16
|
Emitter.$emit = Emitter.emit;
|
|
11
17
|
|
|
12
18
|
Emitter.emit = (name, data) => {
|
|
13
19
|
const id = shortid();
|
|
14
20
|
const type = "WebSocket";
|
|
15
|
-
socket.emit(
|
|
21
|
+
socket.to(name).emit(name, { id, data, type });
|
|
16
22
|
//emit the same event locally
|
|
17
23
|
Emitter.$emit(name, data);
|
|
18
24
|
};
|
|
@@ -11,19 +11,22 @@ describe("SocketEmiiter", () => {
|
|
|
11
11
|
SocketServer.listen(port);
|
|
12
12
|
const emmiter = SocketEmiiter(namespace, WebSocket);
|
|
13
13
|
|
|
14
|
-
setTimeout(() => {
|
|
15
|
-
emmiter.emit(eventName, { testPassed: true });
|
|
16
|
-
}, 500);
|
|
17
14
|
const socket = io.connect(`http://localhost:${port}/${namespace}`);
|
|
18
|
-
socket.on("
|
|
19
|
-
|
|
15
|
+
socket.on("connect", () => {
|
|
16
|
+
console.log(`socket connected to namespace: ${namespace}`);
|
|
17
|
+
socket.emit("subscribe", eventName);
|
|
18
|
+
});
|
|
19
|
+
socket.on(eventName, (payload) => {
|
|
20
|
+
expect(payload)
|
|
20
21
|
.to.be.an("object")
|
|
21
|
-
.that.has.all.keys("id", "
|
|
22
|
-
expect(
|
|
23
|
-
expect(dispatch.data).to.deep.equal({ testPassed: true });
|
|
22
|
+
.that.has.all.keys("id", "data", "type");
|
|
23
|
+
expect(payload.data).to.deep.equal({ testPassed: true });
|
|
24
24
|
done();
|
|
25
25
|
});
|
|
26
26
|
socket.on("disconnect", () => console.log("---------> disconnect"));
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
setTimeout(() => {
|
|
29
|
+
emmiter.emit(eventName, { testPassed: true });
|
|
30
|
+
}, 500);
|
|
28
31
|
});
|
|
29
32
|
});
|
|
@@ -70,7 +70,7 @@ describe("Service.module(constructor)", () => {
|
|
|
70
70
|
|
|
71
71
|
expect(mod)
|
|
72
72
|
.to.be.an("Object")
|
|
73
|
-
.that.has.all.keys("on", "emit", "$clearEvent", "before", "after", "test", "test2")
|
|
73
|
+
.that.has.all.keys("on", "once", "emit", "$clearEvent", "destroy", "before", "after", "test", "test2")
|
|
74
74
|
.that.respondsTo("on")
|
|
75
75
|
.that.respondsTo("emit")
|
|
76
76
|
.that.respondsTo("$clearEvent")
|
|
@@ -205,8 +205,10 @@ describe("Service.module(object)", () => {
|
|
|
205
205
|
"action1",
|
|
206
206
|
"action2",
|
|
207
207
|
"on",
|
|
208
|
+
"once",
|
|
208
209
|
"emit",
|
|
209
210
|
"$clearEvent",
|
|
211
|
+
"destroy",
|
|
210
212
|
"before",
|
|
211
213
|
"after"
|
|
212
214
|
)
|