Skip to content

Commit

Permalink
Chore(Chat data): add some chat data [starts #171944748]
Browse files Browse the repository at this point in the history
  • Loading branch information
gadishimwe committed Mar 24, 2020
1 parent 2cefca1 commit 96f91c2
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 170 deletions.
2 changes: 1 addition & 1 deletion src/controllers/auth.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class AuthController {
* @returns {*} data
*/
static async googleFacebookAuthHandler(req, res) {
const token = JwtService.generateToken({ id: req.user.id });
const token = JwtService.generateToken({ id: req.user.id, email: req.user.email });
await UserService.updateUser({ id: req.user.id }, { token });
return res.redirect(`${process.env.FRONTEND_URL}/facebook/redirect?token=${token}`);
}
Expand Down
12 changes: 5 additions & 7 deletions src/controllers/chat.controller.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Op } from 'sequelize';
import { users, io } from '../helpers/eventEmmiters/socket';
import { users } from '../helpers/eventEmmiters/socket';
import ResponseService from '../services/response.service';
import ChatService from '../services/chat.service';
import UserService from '../services/user.service';
Expand All @@ -14,7 +14,7 @@ class ChatController {
*/
static async saveMessage(req, res) {
const newMessage = await ChatService.saveMessage({ ...req.body, sender: req.userData.email });
ChatService.sendMessage(req.body.receiver, req.userData.email, req.body.message);
ChatService.sendMessage(newMessage.get());

ResponseService.setSuccess(200, 'message added successfully', newMessage);
ResponseService.send(res);
Expand Down Expand Up @@ -46,7 +46,7 @@ class ChatController {
const message = await ChatService.getMessages({
[Op.or]: [{ receiver: req.userData.email, sender: chatUser },
{ sender: req.userData.email, receiver: chatUser }]
});
}, { receiver: req.userData.email, sender: chatUser });

ResponseService.setSuccess(200, 'messages fetched successfully', message);
ResponseService.send(res);
Expand All @@ -68,7 +68,8 @@ class ChatController {
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
profilePicture: user.profilePicture
profilePicture: user.profilePicture,
lastActivity: user.lastActivity
};

const onlineUser = users[user.email];
Expand All @@ -83,9 +84,6 @@ class ChatController {

const sortedUsers = chatUsers.sort((a, b) => a.isOnline.toString()
.localeCompare(b.isOnline.toString())).reverse();

io.emit('updateUsersList', sortedUsers);

ResponseService.setSuccess(200, 'All users', sortedUsers);
return ResponseService.send(res);
}
Expand Down
30 changes: 7 additions & 23 deletions src/helpers/eventEmmiters/socket.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import moment from 'moment';
// eslint-disable-next-line import/no-cycle
import ChatService from '../../services/chat.service';

const io = require('socket.io')();

const socketioJwt = require('socketio-jwt');

const generateMessage = (from, text) => ({
from,
text,
createdAt: moment().valueOf()
});

const users = {};
const startSocket = (server) => {
io.attach(server);
let userName;

io.sockets
.on('connection', socketioJwt.authorize({ // Verify authorization for token
secret: process.env.SECRET_KEY,
Expand All @@ -25,22 +19,12 @@ const startSocket = (server) => {
socket.on('new-user', (user) => { // save a new user for future needs
userName = user.email;
users[user.email] = socket;
socket.broadcast.emit('user-connected', generateMessage('Admin', `${user.firstName} ${user.lastName} Joined!`));
});
// socket.on('private message', (msg) => {
// socket.emit('newMessage', generateMessage(`You --> ${msg.to}`, msg.text));
// users[msg.to].emit('newMessage', generateMessage(`${userName}--> ${msg.to}`, msg.text));
// });
socket.on('private message', (msg) => {
socket.emit('newMessage', msg);
if (users[msg.receiver]) {
users[msg.receiver].emit('newMessage', msg);
}
});
socket.on('chat_history', (msg) => {
socket.emit('newMessage', msg);
socket.broadcast.emit('user-connected', user);
});
socket.on('disconnect', () => {
socket.on('disconnect', async () => {
const response = await ChatService.updateLastActivity(userName);
const { lastActivity } = response[1][0];
socket.broadcast.emit('user-disconnected', { userEmail: userName, lastActivity });
delete users[userName];
});
});
Expand Down
3 changes: 3 additions & 0 deletions src/migrations/20200323150805-User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const up = (queryInterface, Sequelize) => queryInterface.addColumn('Users', 'lastActivity', Sequelize.DATE);

export const down = (queryInterface) => queryInterface.removeColumn('Users', 'lastActivity');
1 change: 1 addition & 0 deletions src/models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default (sequelize, DataTypes) => {
profilePicture: DataTypes.STRING(1234),
token: DataTypes.STRING,
isVerified: DataTypes.BOOLEAN,
lastActivity: DataTypes.DATE
}, {});
Users.associate = (models) => {
// associations can be defined here
Expand Down
37 changes: 26 additions & 11 deletions src/services/chat.service.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Op } from 'sequelize';
import models from '../models';
// eslint-disable-next-line import/no-cycle
import { users } from '../helpers/eventEmmiters/socket';

const { Chat } = models;
const { Chat, Users } = models;

/**
* @class ChatService
Expand All @@ -20,18 +21,19 @@ export default class ChatService {

/**
* Get messages.
* @param {object} param notification
* @returns {object} The notification object.
* @param {object} param param
* @param {object} unreadCounter unreadCounter
* @returns {object} messages.
*/
static async getMessages(param) {
static async getMessages(param, unreadCounter) {
const results = await Chat.findAll({
where: param,
order: [['isRead', 'ASC'], ['createdAt', 'ASC']]
});

const unread = await Chat.count({
where: {
...param,
...unreadCounter,
isRead: false
}
});
Expand All @@ -42,14 +44,12 @@ export default class ChatService {
}

/**
* @param {object} receiver
* @param {object} sender
* @param {object} message
* @param {object} newMessage
* @return {function} send messages to connected client
*/
static async sendMessage(receiver, sender, message) {
if (!users[receiver]) return 0;
users[receiver].emit('private message', { sender, message });
static async sendMessage(newMessage) {
if (!users[newMessage.receiver]) return 0;
users[newMessage.receiver].emit('newMessage', newMessage);
}

/**
Expand All @@ -68,4 +68,19 @@ export default class ChatService {
returning: true
});
}

/**
*
*
* @static
* @param {*} email
* @return {object} updated message
* @memberof ChatService
*/
static async updateLastActivity(email) {
return Users.update({ lastActivity: new Date() }, {
where: { email },
returning: true
});
}
}
142 changes: 14 additions & 128 deletions src/tests/notification/socketio.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import faker from 'faker';
import chai from 'chai';
import '../../server';
import NotificationService from '../../services/notification.service';
import { activeUser,
userWithoutManager,
loggedInToken,
loggedInToken2,
activeUserToken,
token } from '../fixtures/users.fixture';
import { activeUser, activeUserToken, } from '../fixtures/users.fixture';


const io = require('socket.io-client');
Expand All @@ -27,136 +22,27 @@ const chatUser1 = { email: activeUser.email,
firstName: faker.name.firstName(),
lastName: faker.name.lastName()
};
const chatUser2 = 'Sally';
const chatUser3 = { email: userWithoutManager.email,
firstName: faker.name.firstName(),
lastName: faker.name.lastName()
};
describe('Chat Server', () => {
/* Test 1 - A Single User */
it('Should broadcast new user once they connect', (done) => {
const client = io.connect(socketURL, options);
const client1 = io.connect(socketURL, options);


NotificationService.sendNotifications('managerEmail', 'message');
client.on('connect', () => {
client.emit('authenticate', { token: activeUserToken });
});
client.on('authenticated', () => {
client.emit('new-user', chatUser1);
client.emit('chat_history', 'chatUser2');
client1.on('user-connected', (message) => {
message.should.be.a('object');
message.text.should.equal(`${chatUser1.firstName} ${chatUser1.lastName} Joined!`);
/* If this client doesn't disconnect it will interfere
with the next test */
client.disconnect();
client1.disconnect();
done();
});
});
});

/* Test 2 - User sends a private message to another user. */
it('Should be able to send private messages', (done) => {
let client1, client2, client3;
const message = { sender: chatUser2.email, receiver: chatUser1.email, text: 'Private Hello World', createdAt: '2020-03-16 12:41:39.948+02' };
let messages = 0;

const completeTest = () => {
messages.should.equal(3);
client1.disconnect();
client2.disconnect();
client3.disconnect();
done();
};

const checkPrivateMessage = (client) => {
client.on('newMessage', (msg) => {
message.text.should.equal(msg.text);
messages += 1;
if (client === client1) {
/* The first client has received the message
we give some time to ensure that the others
will not receive the same message. */
setTimeout(completeTest, 40);
}
});
};

client1 = io.connect(socketURL, options);
checkPrivateMessage(client1);

client1.on('connect', () => {
client1.emit('authenticate', { token: loggedInToken });
client1.on('authenticated', () => {
client1.emit('new-user', chatUser1);

client2 = io.connect(socketURL, options);
checkPrivateMessage(client2);
// connecting client 3
client2.on('connect', () => {
client2.emit('authenticate', { token });
client2.on('authenticated', () => {
client2.emit('new-user', chatUser2);
// client2.emit('chat_history', chatUser2);
client3 = io.connect(socketURL, options);
checkPrivateMessage(client3);
// connecting client 3
client3.on('connect', () => {
client3.emit('authenticate', { token: loggedInToken2 });
client3.on('authenticated', () => {
client3.emit('new-user', chatUser3);
client3.emit('private message', message);
client3.emit('private message', { ...message, receiver: 'example@test.rw' });
});
});
});
client.emit('authenticate', { token: activeUserToken }).on('authenticated', () => {
client.on('success', userData => {
client.emit('new-user', userData);
});
});
});
});
it('Should not broadcast to unconnected user connect', (done) => {
let client1, client3;
const message = { sender: chatUser2.email, receiver: chatUser1.email, text: 'Private Hello World', createdAt: '2020-03-16 12:41:39.948+02' };
let messages = 0;

const completeTest = () => {
messages.should.equal(2);
client1.disconnect();
client3.disconnect();
done();
};

const checkPrivateMessage = (client) => {
client.on('newMessage', (msg) => {
message.text.should.equal(msg.text);
messages += 1;
if (client === client1) {
/* The first client has received the message
we give some time to ensure that the others
will not receive the same message. */
setTimeout(completeTest, 40);
}
});
};

client1 = io.connect(socketURL, options);
checkPrivateMessage(client1);
client1.on('connect', () => {
client1.emit('authenticate', { token: loggedInToken });
client1.on('authenticated', () => {
client1.emit('new-user', chatUser1);
client3 = io.connect(socketURL, options);
checkPrivateMessage(client3);
// connecting client 3
client3.on('connect', () => {
client3.emit('authenticate', { token: loggedInToken2 });
client3.on('authenticated', () => {
client3.emit('new-user', chatUser3);
NotificationService.sendNotifications('managerEmail', message);
client3.emit('private message', message);
});
NotificationService.sendNotifications('managerEmail', 'message');
client1.on('user-connected', (user) => {
user.should.be.a('object');
user.email.should.equal(chatUser1.email);
/* If this client doesn't disconnect it will interfere
with the next test */
client.disconnect();
client1.disconnect();
done();
});
});
});
Expand Down

0 comments on commit 96f91c2

Please sign in to comment.