From 02b62ed018f26c6ee01c10ac34049f528e5fd522 Mon Sep 17 00:00:00 2001 From: Josh Burman Date: Mon, 18 Mar 2019 16:54:16 -0400 Subject: [PATCH] schema validation, yeeee --- package-lock.json | 41 ++++++++++++++++++++++++++++++++ package.json | 2 ++ src/channelManager.ts | 15 +++++++++++- src/channels/sites/mhsChannel.ts | 27 ++++++++++++++++----- src/clientManager.ts | 4 ++-- src/clients/clientBase.ts | 7 ++++-- src/clients/sites/mhsClient.ts | 5 ++-- src/server.ts | 4 ++++ src/test/channelBase.spec.ts | 4 +++- src/test/channelManager.spec.ts | 4 +++- src/test/clientBase.spec.ts | 4 +++- src/test/clientManager.spec.ts | 5 ++-- 12 files changed, 103 insertions(+), 19 deletions(-) diff --git a/package-lock.json b/package-lock.json index 918490b..8fa1563 100755 --- a/package-lock.json +++ b/package-lock.json @@ -95,6 +95,11 @@ "@types/range-parser": "*" } }, + "@types/joi": { + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/@types/joi/-/joi-14.3.2.tgz", + "integrity": "sha512-riJTpNwDOoRgRrrOjfcjwMBYJLVg8rzwMEAAcZ2JBvdmYWCLIsbzTFXHV517terbYzGmdzkwFYeXUem7eXVNoQ==" + }, "@types/jsonwebtoken": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz", @@ -1247,6 +1252,11 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hoek": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.2.tgz", + "integrity": "sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q==" + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -1484,6 +1494,14 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "isemail": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-3.2.0.tgz", + "integrity": "sha512-zKqkK+O+dGqevc93KNsbZ/TqTUFd46MwWjYOoMrjIMZ51eU7DtQG3Wmd9SQQT7i7RVnuTPEiYEWHU3MSbxC1Tg==", + "requires": { + "punycode": "2.x.x" + } + }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1496,6 +1514,16 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, + "joi": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-14.3.1.tgz", + "integrity": "sha512-LQDdM+pkOrpAn4Lp+neNIFV3axv1Vna3j38bisbQhETPMANYRbFJFUyOZcOClYvM/hppMhGWuKSFEK9vjrB+bQ==", + "requires": { + "hoek": "6.x.x", + "isemail": "3.x.x", + "topo": "3.x.x" + } + }, "js-yaml": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", @@ -2192,6 +2220,11 @@ "once": "^1.3.1" } }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -2708,6 +2741,14 @@ "repeat-string": "^1.6.1" } }, + "topo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/topo/-/topo-3.0.3.tgz", + "integrity": "sha512-IgpPtvD4kjrJ7CRA3ov2FhWQADwv+Tdqbsf1ZnPUSAtCJ9e1Z44MmoSGDXGk4IppoZA7jd/QRkNddlLJWlUZsQ==", + "requires": { + "hoek": "6.x.x" + } + }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", diff --git a/package.json b/package.json index dbd6e2d..c330ce6 100755 --- a/package.json +++ b/package.json @@ -17,10 +17,12 @@ }, "homepage": "https://github.com/yardstick/braid#readme", "dependencies": { + "@types/joi": "^14.3.2", "@types/mocha": "^5.2.6", "body-parser": "^1.18.3", "cors": "^2.8.5", "express": "^4.16.4", + "joi": "^14.3.1", "jsonwebtoken": "^8.5.0", "websocket": "^1.0.28", "winston": "^3.2.1", diff --git a/src/channelManager.ts b/src/channelManager.ts index 18eeddb..ee7490d 100644 --- a/src/channelManager.ts +++ b/src/channelManager.ts @@ -6,7 +6,7 @@ import MHSChannel from './channels/sites/mhsChannel'; var logger = require('./logger'); class ChannelManager { - channels: ChannelBase[]|MHSChannel[] = []; + channels: any = []; constructor() { // create default channel... @@ -85,6 +85,19 @@ class ChannelManager { return false; } + + purgeEmptyChannels() { + var index = 0 + + for (let channel of this.channels) { + if (channel.clients.length == 0) { + this.channels.splice(index, 1); + logger.accessLog.info(`channel removed: ${channel.id}`); + } + + index++; + } + } }; export default ChannelManager; diff --git a/src/channels/sites/mhsChannel.ts b/src/channels/sites/mhsChannel.ts index 267ab79..3d9ba30 100644 --- a/src/channels/sites/mhsChannel.ts +++ b/src/channels/sites/mhsChannel.ts @@ -1,17 +1,32 @@ 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) { - 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}: `, {message: message}); - } else { - logger.accessLog.info(`client either not a teacher or is the sender: ${client.id} - `, {message: message}); + 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}: `, {message: message}); + } else { + logger.accessLog.info(`client either not a teacher or is the sender: ${client.id} - `, {message: message}); + } } } diff --git a/src/clientManager.ts b/src/clientManager.ts index b50a169..92e5e88 100644 --- a/src/clientManager.ts +++ b/src/clientManager.ts @@ -69,11 +69,11 @@ class ClientManager { 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); + return new Client(data, ws, this); } catch (e) { logger.errorLog.info(e); logger.accessLog.info(`creating base client: ${data.user_id}`); - return new ClientBase(data, ws); + return new ClientBase(data, ws, this); } } }; diff --git a/src/clients/clientBase.ts b/src/clients/clientBase.ts index 8e6df4f..1ed916b 100644 --- a/src/clients/clientBase.ts +++ b/src/clients/clientBase.ts @@ -1,5 +1,6 @@ import * as WebSocket from 'ws'; import * as ChannelManager from '../channelManager'; +import ClientManager from '../clientManager'; import ChannelBase from '../channels/channelBase'; import MHSChannel from '../channels/sites/mhsChannel'; @@ -10,12 +11,14 @@ class ClientBase { data: any; id: number; channel: ChannelBase|MHSChannel|null; + clientManager: ClientManager; - constructor(data: any, ws: WebSocket) { + constructor(data: any, ws: WebSocket, clientManager: ClientManager) { this.ws = ws; this.data = data; this.id = data.user_id; this.channel = null; + this.clientManager = clientManager; } getData() { @@ -46,7 +49,7 @@ class ClientBase { this.channel.removeClient(this.id); } - // server.clientManager.removeClient(this.id); + this.clientManager.removeClient(this.id); logger.accessLog.info(`closed connection for client ${this.id}`); }); } diff --git a/src/clients/sites/mhsClient.ts b/src/clients/sites/mhsClient.ts index dcc86e6..aee05f4 100644 --- a/src/clients/sites/mhsClient.ts +++ b/src/clients/sites/mhsClient.ts @@ -1,11 +1,12 @@ import * as WebSocket from 'ws'; import ClientBase from '../clientBase'; +import ClientManager from '../../clientManager'; var logger = require('../../logger'); class MHSClient extends ClientBase { - constructor(data: any, ws: WebSocket) { - super(data, ws); + constructor(data: any, ws: WebSocket, clientManager: ClientManager) { + super(data, ws, clientManager); logger.accessLog.info('MHS Client Created', {data: data}); } }; diff --git a/src/server.ts b/src/server.ts index bbd2782..c311439 100755 --- a/src/server.ts +++ b/src/server.ts @@ -52,6 +52,10 @@ function connectionManager() { channelManager.addClientToChannel(client, data.channel); } + //remove any channels that have no users in them + logger.accessLog.info(`Purging empty channels...`); + channelManager.purgeEmptyChannels(); + ws.send(`Hi there, welcome to braid, Measures Web Socket server. Connecting all our services!\nYou are currently in channel: ${data.channel}`); }); } diff --git a/src/test/channelBase.spec.ts b/src/test/channelBase.spec.ts index 0149664..3de19ad 100644 --- a/src/test/channelBase.spec.ts +++ b/src/test/channelBase.spec.ts @@ -1,5 +1,6 @@ import ChannelBase from '../channels/channelBase'; import ClientBase from '../clients/clientBase'; +import ClientManager from '../clientManager'; var exec = require('child_process').exec; var expect = require('chai').expect; @@ -10,8 +11,9 @@ var app = require('../config/app'); var name: string = 'test channel'; var wsClient = new WebSocketClient(); var channel: ChannelBase; +var clientManager = new ClientManager(); var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } -var client: ClientBase = new ClientBase(data, wsClient); +var client: ClientBase = new ClientBase(data, wsClient, 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 e54898f..360113e 100644 --- a/src/test/channelManager.spec.ts +++ b/src/test/channelManager.spec.ts @@ -3,6 +3,7 @@ import ClientBase from '../clients/clientBase'; import MHSClient from '../clients/sites/mhsClient'; import MHSChannel from '../channels/sites/mhsChannel'; import ChannelManager from '../channelManager'; +import ClientManager from '../clientManager'; var expect = require('chai').expect; var sinon = require('sinon'); @@ -11,7 +12,8 @@ var name: string = 'test channel'; var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); -var client: ClientBase = new ClientBase(data, wsClient); +var clientManager = new ClientManager(); +var client: ClientBase = new ClientBase(data, wsClient, clientManager); let channelManager = new ChannelManager(); diff --git a/src/test/clientBase.spec.ts b/src/test/clientBase.spec.ts index e74a8dc..904c33e 100644 --- a/src/test/clientBase.spec.ts +++ b/src/test/clientBase.spec.ts @@ -1,5 +1,6 @@ import * as WebSocket from 'ws'; import ClientBase from '../clients/clientBase'; +import ClientManager from '../clientManager'; var expect = require('chai').expect; var assert = require('chai').assert; @@ -9,7 +10,8 @@ var name: string = 'test channel'; var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); -var client: ClientBase = new ClientBase(data, wsClient); +var clientManager = new ClientManager(); +var client: ClientBase = new ClientBase(data, wsClient, 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 61f965b..382bda5 100644 --- a/src/test/clientManager.spec.ts +++ b/src/test/clientManager.spec.ts @@ -11,9 +11,8 @@ var name: string = 'test channel'; var data: any = { 'client': 'test', 'client_type':'site', 'user_id': 125, 'user_type': 'user', 'channel': name } var WebSocketClient = require('websocket').client; var wsClient = new WebSocketClient(); -var client: ClientBase = new ClientBase(data, wsClient); - -let clientManager = new ClientManager(); +var clientManager = new ClientManager(); +var client: ClientBase = new ClientBase(data, wsClient, clientManager); describe('ClientManager', function () { it('should add a client', function () {