diff --git a/src/channelManager.ts b/src/channelManager.ts index 08a229d..6dc054c 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -3,7 +3,10 @@ import ClientBase from './clients/clientBase'; import MHSClient from './clients/sites/mhsClient'; import MHSChannel from './channels/sites/mhsChannel'; +import * as Joi from 'joi'; + var logger = require('./logger'); +var app = require('./config/app'); class ChannelManager { channels: any = []; @@ -66,11 +69,8 @@ class ChannelManager { } removeClientFromChannel(client_id: number, channel_id: string) { - var index: number = 0; - for (let channel of this.channels) { if (channel.id == channel_id) { - this.channels.splice(index, 1); if (channel.removeClient(client_id)) { logger.accessLog.info(`client removed from channel - channel: ${channel_id}, client: ${client_id}`); return true; @@ -79,23 +79,29 @@ class ChannelManager { return false } } - - index++; } return false; } - purgeEmptyChannels() { - var index = 0 + changeChannel(client: ClientBase|MHSClient, changeRequest: any) { + if (client.channel != null) { + this.removeClientFromChannel(client.id, client.channel.id) + } + var channel = this.createChannel(changeRequest); + this.addClientToChannel(client, channel.id); + this.purgeEmptyChannels(); + console.log(this.channels) + } + + purgeEmptyChannels() { for (let channel of this.channels) { if (channel.clients.length == 0 && channel.id != 'default') { + var index = this.channels.indexOf(channel) this.channels.splice(index, 1); logger.accessLog.info(`channel removed: ${channel.id}`); } - - index++; } } }; diff --git a/src/channels/channelBase.ts b/src/channels/channelBase.ts index ce85ff2..904f143 100644 --- a/src/channels/channelBase.ts +++ b/src/channels/channelBase.ts @@ -34,15 +34,12 @@ class ChannelBase { } removeClient(id: number) { - var index: number = 0; - for (let client of this.clients) { if (client.id == id) { + var index = this.clients.indexOf(client) this.clients.splice(index, 1); return true; } - - index++; } return false; diff --git a/src/channels/sites/mhsChannel.ts b/src/channels/sites/mhsChannel.ts index c8787fd..985ba22 100644 --- a/src/channels/sites/mhsChannel.ts +++ b/src/channels/sites/mhsChannel.ts @@ -1,32 +1,17 @@ import ClientBase from '../../clients/clientBase'; import MHSClient from '../../clients/sites/mhsClient'; import ChannelBase from '../channelBase'; -import * as Joi from 'joi'; var logger = require('../../logger'); class MHSChannel extends ChannelBase { - private schema = { - currentIndex: Joi.number().integer().required(), - totalQuestions: Joi.number().integer().required(), - timeElapsed: Joi.number().required(), - status: Joi.string().required(), - }; - - broadcastMessage(from: ClientBase|MHSClient|null, message: string) { - const result = Joi.validate(JSON.parse(message), this.schema); - - if (result.error && from != null) { - from.ws.send(`Failed to send the message, the data provided does not conform to the schema outlined by the channel type (mhs) - ${result.error}`); - logger.errorLog.info(`Failed to send the message, the data provided does not conform to the schema outlined by the channel type (mhs)"`, {data: {clientId: from.id, message: message, error: result.error}}); - } else { - for (let client of this.clients) { - if (client != from && client.data.user_type == 'teacher') { - client.ws.send(message); - logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }}); - } else { - logger.accessLog.info(`client either not a teacher or is the sender: ${client.id}`, { data: { message: message }}); - } + broadcastMessage(from: ClientBase|MHSClient|null, message: any) { + for (let client of this.clients) { + if (client != from && client.data.user_type == 'teacher') { + client.ws.send(JSON.stringify(message)); + logger.accessLog.info(`sent to ${client.id}`, { data: { message: message }}); + } else { + logger.accessLog.info(`client either not a teacher or is the sender: ${client.id}`, { data: { message: message }}); } } diff --git a/src/clientManager.ts b/src/clientManager.ts index 92e5e88..a2cfd64 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -1,6 +1,7 @@ import * as WebSocket from 'ws'; import ClientBase from './clients/clientBase'; import MHSClient from './clients/sites/mhsClient'; +import ChannelManager from './channelManager'; var logger = require('./logger'); @@ -11,9 +12,9 @@ class ClientManager { //...maybe one day } - addClient(data: any, ws: WebSocket) { + addClient(data: any, channelManager: ChannelManager, ws: WebSocket) { if (data.client_type && !this.clientExists(data.user_id)) { - var client = this.getClientType(data, ws); + var client = this.getClientType(data, channelManager, ws); this.clients.push(client); logger.accessLog.info(`client added to client manager: ${data.user_id}`); return client; @@ -65,15 +66,15 @@ class ClientManager { } } - getClientType(data: any, ws: WebSocket) { + getClientType(data: any, channelManager: ChannelManager, ws: WebSocket) { try { var Client = require(`./clients/${data.client_type}s/${data.client}client`); logger.accessLog.info(`attempting to create client of type ${data.client}, client id: ${data.user_id}...`); - return new Client(data, ws, this); + return new Client(data, ws, channelManager, this); } catch (e) { logger.errorLog.info(e); logger.accessLog.info(`creating base client: ${data.user_id}`); - return new ClientBase(data, ws, this); + return new ClientBase(data, ws, channelManager, this); } } }; diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 1ed916b..99af8ac 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -1,9 +1,10 @@ import * as WebSocket from 'ws'; -import * as ChannelManager from '../channelManager'; import ClientManager from '../clientManager'; +import ChannelManager from '../channelManager'; import ChannelBase from '../channels/channelBase'; import MHSChannel from '../channels/sites/mhsChannel'; +var messageManager = require('../messageManager'); var logger = require('../logger'); class ClientBase { @@ -12,13 +13,15 @@ class ClientBase { id: number; channel: ChannelBase|MHSChannel|null; clientManager: ClientManager; + channelManager: ChannelManager; - constructor(data: any, ws: WebSocket, clientManager: ClientManager) { + constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { this.ws = ws; this.data = data; this.id = data.user_id; this.channel = null; this.clientManager = clientManager; + this.channelManager = channelManager; } getData() { @@ -36,11 +39,25 @@ class ClientBase { connectToChannel(channel: ChannelBase|MHSChannel) { this.channel = channel; - this.ws.on('message', (message: string) => { - logger.accessLog.info(`starting broadcast on channel ${channel.id}: `, {message: message}); - channel.broadcastMessage(this, message); - logger.accessLog.info(`broadcast complete on channel ${channel.id}: `, {message: message}); - }); + var messageListener = (message: any) => { + logger.accessLog.info(`starting message transaction on channel ${channel.id}: `, {message: message}); + this.ws.removeListener('message', messageListener); + message = messageManager.prepareMessage(message) + + if (!message.error) { + if (message['message_type'] == 'broadcast') { + channel.broadcastMessage(this, message); + } else if (message['message_type'] == 'changeChannel') { + this.channelManager.changeChannel(this, message); + } + + logger.accessLog.info(`message transaction complete on channel ${channel.id}: `, {message: message}); + } else { + logger.errorLog.info(`Validation failed, please review schema: ${channel.id}`, {data: {message: message, error: message.error}}); + } + } + + this.ws.on('message', messageListener); this.ws.on('close', (reasonCode: string, description: string) => { logger.accessLog.info(`closing connection for client ${this.id}`); diff --git a/src/clients/sites/mhsClient.ts b/src/clients/sites/mhsClient.ts index aee05f4..596ee91 100644 --- a/src/clients/sites/mhsClient.ts +++ b/src/clients/sites/mhsClient.ts @@ -1,12 +1,13 @@ import * as WebSocket from 'ws'; import ClientBase from '../clientBase'; import ClientManager from '../../clientManager'; +import ChannelManager from '../../channelManager'; var logger = require('../../logger'); class MHSClient extends ClientBase { - constructor(data: any, ws: WebSocket, clientManager: ClientManager) { - super(data, ws, clientManager); + constructor(data: any, ws: WebSocket, channelManager: ChannelManager, clientManager: ClientManager) { + super(data, ws, channelManager, clientManager); logger.accessLog.info('MHS Client Created', {data: data}); } }; diff --git a/src/config/app.ts b/src/config/app.ts index 72882ea..6b909d2 100644 --- a/src/config/app.ts +++ b/src/config/app.ts @@ -14,5 +14,6 @@ module.exports = { subject: 'Braid JWT', audience: 'internal', algorithm: ["HS256"] - } + }, + messageTypes : ['broadcast', 'changeChannel'] } diff --git a/src/messageManager.ts b/src/messageManager.ts new file mode 100644 index 0000000..91c8bd3 --- /dev/null +++ b/src/messageManager.ts @@ -0,0 +1,29 @@ +import * as Joi from 'joi'; + +var logger = require('./logger'); +var app = require('./config/app'); + +let schema = { + message_type: Joi.string().valid(app.messageTypes).insensitive().required(), + current_index: Joi.number().integer(), + total_questions: Joi.number().integer(), + time_elapsed: Joi.number(), + status: Joi.string(), + channel: Joi.string(), + client: Joi.string(), + client_type: Joi.string(), + user_id: Joi.number().integer(), +}; + +module.exports = { + prepareMessage: (message: string) => { + var parsed = JSON.parse(message) + const result = Joi.validate(parsed, schema); + + if (result.error) { + return result + } else { + return parsed + } + } +} diff --git a/src/server.ts b/src/server.ts index c311439..2016ad4 100755 --- a/src/server.ts +++ b/src/server.ts @@ -45,7 +45,7 @@ function connectionManager() { client.replaceWebSocket(ws); } } else { - var client: ClientBase|MHSClient|null = clientManager.addClient(data, ws); + var client: ClientBase|MHSClient|null = clientManager.addClient(data, channelManager, ws); } if (client != null) { diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index 3de19ad..806fa30 100644 --- a/src/test/channelBase.spec.ts +++ b/src/test/channelBase.spec.ts @@ -1,6 +1,7 @@ import ChannelBase from '../channels/channelBase'; import ClientBase from '../clients/clientBase'; import ClientManager from '../clientManager'; +import ChannelManager from '../channelManager'; var exec = require('child_process').exec; var expect = require('chai').expect; @@ -12,8 +13,9 @@ var name: string = 'test channel'; var wsClient = new WebSocketClient(); var channel: ChannelBase; var clientManager = new ClientManager(); +var channelManager = new ChannelManager(); var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } -var client: ClientBase = new ClientBase(data, wsClient, clientManager); +var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager); describe('ChannelBase', function () { it('should create a class of ChannelBase', function () { diff --git a/src/test/channelManager.spec.ts b/src/test/channelManager.spec.ts index 360113e..ae98652 100644 --- a/src/test/channelManager.spec.ts +++ b/src/test/channelManager.spec.ts @@ -13,9 +13,8 @@ var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_ var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); var clientManager = new ClientManager(); -var client: ClientBase = new ClientBase(data, wsClient, clientManager); - -let channelManager = new ChannelManager(); +var channelManager = new ChannelManager(); +var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager); describe('ChannelManager', function () { var channel: ChannelBase; diff --git a/src/test/clientBase.spec.ts b/src/test/clientBase.spec.ts index 904c33e..0287ddd 100644 --- a/src/test/clientBase.spec.ts +++ b/src/test/clientBase.spec.ts @@ -1,6 +1,7 @@ import * as WebSocket from 'ws'; import ClientBase from '../clients/clientBase'; import ClientManager from '../clientManager'; +import ChannelManager from '../channelManager'; var expect = require('chai').expect; var assert = require('chai').assert; @@ -11,7 +12,8 @@ var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_ var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); var clientManager = new ClientManager(); -var client: ClientBase = new ClientBase(data, wsClient, clientManager); +var channelManager = new ChannelManager(); +var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager); describe('ClientBase', function () { it('should get client data', function () { diff --git a/src/test/clientManager.spec.ts b/src/test/clientManager.spec.ts index 382bda5..4d10d28 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -2,6 +2,7 @@ import * as WebSocket from 'ws'; import ClientBase from '../clients/clientBase'; import MHSClient from '../clients/sites/mhsClient'; import ClientManager from '../clientManager'; +import ChannelManager from '../channelManager'; var expect = require('chai').expect; var assert = require('chai').assert; @@ -12,11 +13,13 @@ var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_ var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); var clientManager = new ClientManager(); -var client: ClientBase = new ClientBase(data, wsClient, clientManager); +var channelManager = new ChannelManager(); + +var client: ClientBase = new ClientBase(data, wsClient, channelManager, clientManager); describe('ClientManager', function () { it('should add a client', function () { - var result = clientManager.addClient(data, wsClient); + var result = clientManager.addClient(data, channelManager, wsClient); expect(result.id).to.be.equal(125); }); @@ -51,7 +54,7 @@ describe('ClientManager', function () { it('should add client of type MHSClient', function () { var data: any = { 'client': 'mhs', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } - var result = clientManager.getClientType(data, wsClient); + var result = clientManager.getClientType(data, channelManager, wsClient); expect(result.clientType()).to.be.equal('mhs'); }); });