Skip to content

Commit 20e07c7

Browse files
author
Andrew Isherwood
committed
Ensure WS messages are sent to correct recipients
In most cases, we don't want to send a websocket message to all connected clients. The messages clients should receive depends on the user type and the user's association (if any) with the object the message pertains to. This commit ensures that only appropriate clients receive messages. Includes: - Added new WebocketServer.recipientsMessage method. This method receives an array of user IDs that should receive the message, it then iterates through this and sends the message to any connected clients in that list. - Added "who should receive this message" logic to all side effect functions, mostly making use of the new 'associated' DB resolver
1 parent a8516ea commit 20e07c7

File tree

6 files changed

+109
-47
lines changed

6 files changed

+109
-47
lines changed

classes/WebsocketServer.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ class WebsocketServer {
4242
// Return an array of all connected client user IDs
4343
connectedClientUserIds() {
4444
const ids = [];
45-
this.socketServer.clients.forEach((client) => ids.push(client.userId));
45+
this.socketServer.clients.forEach(
46+
(client) => ids.push(client.userId)
47+
);
4648
return ids;
4749
}
4850
// Send a message to all available clients
@@ -61,6 +63,20 @@ class WebsocketServer {
6163
}
6264
});
6365
}
66+
// Send a message to a specific list of recipients, as long as they're
67+
// connected
68+
recipientsMessage({ recipients, initiator, subject, action, payload }) {
69+
this.socketServer.clients.forEach((client) => {
70+
if (
71+
recipients.indexOf(client.userId) > -1 &&
72+
client.readyState === WebSocket.OPEN
73+
) {
74+
client.send(
75+
JSON.stringify({ initiator, subject, action, payload })
76+
);
77+
}
78+
});
79+
}
6480
// Send a message to all client *except* the one that initiated
6581
// the action
6682
excludeInitiatorMessage({ initiator, subject, action, payload }) {

middleware/side-effects/folders.js

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,26 @@ const WebsocketServer = require('../../classes/WebsocketServer');
22
const db = require('../../../ems-db');
33

44
const folders = {
5-
// Send updated folder counts to anyone who is connected
6-
// and can see the given query
5+
// Send updated folder counts to any staff connected
76
folderCountsToClients: async (req, res, next) => {
8-
// Get the user objects associated with connected clients
97
const activeClientIds = WebsocketServer.connectedClientUserIds();
10-
const activeClients = await db.resolvers.users.allUsers({
11-
query: { user_ids: activeClientIds.join('_') }
12-
});
13-
14-
activeClients.rows.forEach((client) => {
15-
// We don't await here because these can be sent async
16-
db.resolvers.folders
17-
.folderCounts({ user: client })
18-
.then((folderCounts) => {
19-
WebsocketServer.onlyInitiatorMessage({
20-
initiator: client.id,
21-
subject: 'folderCount',
22-
action: 'update',
23-
payload: folderCounts
24-
});
25-
})
26-
.catch((err) => err);
8+
// Get the user objects associated with all staff
9+
const allStaff = await db.resolvers.users.allStaff();
10+
allStaff.rows.forEach((staff) => {
11+
if (activeClientIds.indexOf(staff.id) > -1) {
12+
// We don't await here because these can be sent async
13+
db.resolvers.folders
14+
.folderCounts({ user: staff })
15+
.then((folderCounts) => {
16+
WebsocketServer.onlyInitiatorMessage({
17+
initiator: staff.id,
18+
subject: 'folderCount',
19+
action: 'update',
20+
payload: folderCounts
21+
});
22+
})
23+
.catch((err) => err);
24+
}
2725
});
2826
next();
2927
}

middleware/side-effects/labels.js

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,43 @@ const WebsocketServer = require('../../classes/WebsocketServer');
22
const db = require('../../../ems-db');
33

44
const labels = {
5-
// Inform all clients, apart from the initiator, that
5+
// Inform all staff, apart from the initiator, that
66
// a label has been created or updated
7-
labelToClients: (req, res, next) => {
7+
labelToClients: async (req, res, next) => {
88
const methodToAction = {
99
POST: 'create',
1010
PUT: 'update',
1111
DELETE: 'delete'
1212
};
1313
const { label } = req.wsData;
14-
// Send the new message via the websocket
15-
WebsocketServer.excludeInitiatorMessage({
14+
// Get the recipients
15+
const allStaff = await db.resolvers.users.allStaff();
16+
const recipients = allStaff.rows
17+
.filter((staff) => staff.id !== req.user.id)
18+
.map((toMap) => toMap.id);
19+
20+
WebsocketServer.recipientsMessage({
21+
recipients,
1622
initiator: req.user.id,
1723
subject: 'label',
1824
action: methodToAction[req.method],
1925
payload: label
2026
});
2127
next();
2228
},
23-
// Send updated label counts to anyone who is connected
24-
// and can see the given query
29+
// Send updated label counts to all staff
2530
labelCountsToClients: async (req, res, next) => {
26-
// Get the user objects associated with connected clients
27-
const activeClientIds = WebsocketServer.connectedClientUserIds();
28-
const activeClients = await db.resolvers.users.allUsers({
29-
query: { user_ids: activeClientIds.join('_') }
30-
});
31+
// Get the recipients
32+
const allStaff = await db.resolvers.users.allStaff();
3133

32-
activeClients.rows.forEach((client) => {
33-
// We don't await here because these can be sent async
34+
// Iterate through each recipient
35+
allStaff.rows.forEach((recipient) => {
36+
// Get this recipient's label counts
3437
db.resolvers.labels
35-
.labelCounts({ user: client })
38+
.labelCounts({ user: recipient })
3639
.then((labelCounts) => {
3740
WebsocketServer.onlyInitiatorMessage({
38-
initiator: client.id,
41+
initiator: recipient.id,
3942
subject: 'labelCount',
4043
action: 'update',
4144
payload: labelCounts

middleware/side-effects/messages.js

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,45 @@
11
const WebsocketServer = require('../../classes/WebsocketServer');
22

3+
const db = require('../../../ems-db');
4+
35
const messages = {
4-
// Inform all clients, apart from the initiator, that
6+
// Inform all participants, apart from the initiator, that
57
// a message has been created or updated
6-
newMessageToClients: (req, res, next) => {
8+
newMessageToClients: async (req, res, next) => {
79
const socketAction = req.method === 'POST' ? 'create' : 'update';
810
const { message } = req.wsData;
11+
// Find out who should receive this message
12+
const allAssociated = await db.resolvers.queries.associated(
13+
[message.query_id]
14+
);
15+
const recipients = allAssociated.filter(
16+
(associated) => associated !== req.user.id
17+
);
18+
console.log(recipients);
919
// Send the new message via the websocket
10-
WebsocketServer.excludeInitiatorMessage({
20+
WebsocketServer.recipientsMessage({
21+
recipients,
1122
initiator: message.creator_id,
1223
subject: 'message',
1324
action: socketAction,
1425
payload: message
1526
});
1627
next();
1728
},
18-
// Inform all clients, apart from the initiator, that
29+
// Inform all participants, apart from the initiator, that
1930
// a message has been deleted
20-
deletedMessageToClients: (req, res, next) => {
31+
deletedMessageToClients: async (req, res, next) => {
2132
const { message } = req.wsData;
33+
// Find out who should receive this message
34+
const allAssociated = await db.resolvers.queries.associated(
35+
[message.query_id]
36+
);
37+
const recipients = allAssociated.filter(
38+
(associated) => associated !== req.user.id
39+
);
2240
// Send the deleted message details via the websocket
23-
WebsocketServer.excludeInitiatorMessage({
41+
WebsocketServer.recipientsMessage({
42+
recipients,
2443
initiator: message.creator_id,
2544
subject: 'message',
2645
action: 'delete',

middleware/side-effects/queries.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@ const queries = {
66
createdQueryToClients: async (req, res, next) => {
77
const { queries } = req.wsData;
88
// Send the updated query via the websocket
9-
// but not to the user who created the message
10-
WebsocketServer.excludeInitiatorMessage({
9+
// Find out who should receive this message
10+
const queryIds = queries.map((query) => query.id);
11+
const allAssociated = await db.resolvers.queries.associated(queryIds);
12+
// Remove the creator of the query, they will have received it in
13+
// the API response
14+
const recipients = allAssociated.filter(
15+
(associated) => associated !== queries[0].initiator
16+
);
17+
WebsocketServer.recipientsMessage({
18+
recipients,
1119
initiator: queries[0].initiator,
1220
subject: 'query',
1321
action: 'create',
@@ -19,8 +27,17 @@ const queries = {
1927
updatedQueriesToClients: async (req, res, next) => {
2028
const { queries } = req.wsData;
2129
const toSend = await helpers.addEmbeds(queries);
30+
// Find out who should receive this message
31+
const queryIds = queries.map((query) => query.id);
32+
const allAssociated = await db.resolvers.queries.associated(queryIds);
33+
// Remove the updater of the query, they will have received it in
34+
// the API response
35+
const recipients = allAssociated.filter(
36+
(associated) => associated !== req.user.id
37+
);
2238
// Send the updated query via the websocket
23-
WebsocketServer.broadcastMessage({
39+
WebsocketServer.recipientsMessage({
40+
recipients,
2441
subject: 'query',
2542
action: 'update',
2643
payload: toSend

middleware/side-effects/uploads.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
11
const WebsocketServer = require('../../classes/WebsocketServer');
2+
const db = require('../../../ems-db');
23

34
const uploads = {
45
// Inform all clients, apart from the initiator, that
56
// one or more uploads have been sent
6-
newUploadToClients: (req, res, next) => {
7+
newUploadToClients: async (req, res, next) => {
78
const { uploads } = req.wsData;
9+
// Find out who should receive this message
10+
const allAssociated = await db.resolvers.queries.associated(
11+
[uploads[0].query_id]
12+
);
13+
const recipients = allAssociated.filter(
14+
(associated) => associated !== req.user.id
15+
);
816
// Send the new message via the websocket
9-
WebsocketServer.excludeInitiatorMessage({
17+
WebsocketServer.recipientsMessage({
18+
recipients,
1019
initiator: uploads[0].creator_id,
1120
subject: 'upload',
1221
action: 'create',

0 commit comments

Comments
 (0)