Forráskód Böngészése

chore(backend): added jsdocs for most functions

Signed-off-by: Jonathan <theflametrooper@gmail.com>
Jonathan 3 éve
szülő
commit
fc28a7564f

+ 10 - 1
backend/.eslintrc

@@ -31,6 +31,15 @@
 		"no-param-reassign": 0,
 		"implicit-arrow-linebreak": 0,
 		"import/extensions": 0,
-		"class-methods-use-this": 0
+		"class-methods-use-this": 0,
+		"require-jsdoc": [2, {
+			"require": {
+				"FunctionDeclaration": true,
+				"MethodDefinition": true,
+				"ClassDeclaration": false,
+				"ArrowFunctionExpression": false,
+				"FunctionExpression": false
+			}
+		}]
     }
 }

+ 74 - 0
backend/core.js

@@ -2,6 +2,7 @@ import async from "async";
 import config from "config";
 
 class DeferredPromise {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		this.promise = new Promise((resolve, reject) => {
 			this.reject = reject;
@@ -11,28 +12,43 @@ class DeferredPromise {
 }
 
 class MovingAverageCalculator {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		this.count = 0;
 		this._mean = 0;
 	}
 
+	/**
+	 * Updates the mean average
+	 *
+	 * @param {number} newValue - the new time it took to complete a job
+	 */
 	update(newValue) {
 		this.count += 1;
 		const differential = (newValue - this._mean) / this.count;
 		this._mean += differential;
 	}
 
+	/**
+	 * Returns the mean average
+	 *
+	 * @returns {number} - returns the mean average
+	 */
 	get mean() {
 		this.validate();
 		return this._mean;
 	}
 
+	/**
+	 * Checks that the mean is valid
+	 */
 	validate() {
 		if (this.count === 0) throw new Error("Mean is undefined");
 	}
 }
 
 export default class CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor(name) {
 		this.name = name;
 		this.status = "UNINITIALIZED";
@@ -50,6 +66,11 @@ export default class CoreClass {
 		this.registerJobs();
 	}
 
+	/**
+	 * Sets the status of a module
+	 *
+	 * @param {string} status - the new status of a module
+	 */
 	setStatus(status) {
 		this.status = status;
 		this.log("INFO", `Status changed to: ${status}`);
@@ -57,18 +78,36 @@ export default class CoreClass {
 		else if (this.status === "FAIL" || this.status === "LOCKDOWN") this.jobQueue.pause();
 	}
 
+	/**
+	 * Returns the status of a module
+	 *
+	 * @returns {string} - the status of a module
+	 */
 	getStatus() {
 		return this.status;
 	}
 
+	/**
+	 * Changes the current stage of a module
+	 *
+	 * @param {string} stage - the new stage of a module
+	 */
 	setStage(stage) {
 		this.stage = stage;
 	}
 
+	/**
+	 * Returns the current stage of a module
+	 *
+	 * @returns {string} - the current stage of a module
+	 */
 	getStage() {
 		return this.stage;
 	}
 
+	/**
+	 * Initialises a module and handles initialise successes and failures
+	 */
 	_initialize() {
 		this.setStatus("INITIALIZING");
 
@@ -84,6 +123,11 @@ export default class CoreClass {
 			});
 	}
 
+	/**
+	 * Creates a new log message
+	 *
+	 * @param {...any} args - anything to be included in the log message, the first argument is the type of log
+	 */
 	log(...args) {
 		const _arguments = Array.from(args);
 		const type = _arguments[0];
@@ -109,6 +153,9 @@ export default class CoreClass {
 		}
 	}
 
+	/**
+	 * Sets up each job with the statistics service (includes mean average for job completion)
+	 */
 	registerJobs() {
 		let props = [];
 		let obj = this;
@@ -129,6 +176,16 @@ export default class CoreClass {
 		});
 	}
 
+	/**
+	 * Runs a job
+	 *
+	 * @param {string} name - the name of the job e.g. GET_PLAYLIST
+	 * @param {object} payload - any expected payload for the job itself
+	 * @param {object} options - object containing any additional options for the job
+	 * @param {boolean} options.isQuiet - whether or not the job should be advertised in the logs, useful for repetitive/unimportant jobs
+	 * @param {boolean} options.bypassQueue - UNKNOWN
+	 * @returns {Promise} - returns a promise
+	 */
 	runJob(name, payload, options = { isQuiet: false, bypassQueue: false }) {
 		const deferredPromise = new DeferredPromise();
 		const job = { name, payload, onFinish: deferredPromise };
@@ -151,10 +208,27 @@ export default class CoreClass {
 		return deferredPromise.promise;
 	}
 
+	/**
+	 * UNKNOWN
+	 *
+	 * @param {object} moduleManager - UNKNOWN
+	 */
 	setModuleManager(moduleManager) {
 		this.moduleManager = moduleManager;
 	}
 
+	/**
+	 * Actually runs the job? UNKNOWN
+	 *
+	 * @param {object} job - object containing details of the job
+	 * @param {string} job.name - the name of the job e.g. GET_PLAYLIST
+	 * @param {string} job.payload - any expected payload for the job itself
+	 * @param {Promise} job.onFinish - deferred promise when the job is complete
+	 * @param {object} options - object containing any additional options for the job
+	 * @param {boolean} options.isQuiet - whether or not the job should be advertised in the logs, useful for repetitive/unimportant jobs
+	 * @param {boolean} options.bypassQueue - UNKNOWN
+	 * @param {Function} cb - Callback after the job has completed
+	 */
 	_runJob(job, options, cb) {
 		if (!options.isQuiet) this.log("INFO", `Running job ${job.name}`);
 

+ 24 - 0
backend/index.js

@@ -231,6 +231,7 @@ if (config.debug && config.debug.traceUnhandledPromises === true) {
 // }
 
 class ModuleManager {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		this.modules = {};
 		this.modulesNotInitialized = [];
@@ -246,6 +247,11 @@ class ModuleManager {
 		};
 	}
 
+	/**
+	 * Adds a new module to the backend server/module manager
+	 *
+	 * @param {string} moduleName - the name of the module (also needs to be the same as the filename of a module located in the logic folder or "logic/moduleName/index.js")
+	 */
 	async addModule(moduleName) {
 		console.log("add module", moduleName);
 		// import(`./logic/${moduleName}`).then(Module => {
@@ -260,6 +266,10 @@ class ModuleManager {
 		this.modules[moduleName] = import(`./logic/${moduleName}`);
 	}
 
+	/**
+	 * Initialises a new module to the backend server/module manager
+	 *
+	 */
 	async initialize() {
 		// if (!this.modules["logger"]) return console.error("There is no logger module");
 		// this.logger = this.modules["logger"];
@@ -301,6 +311,11 @@ class ModuleManager {
 		}
 	}
 
+	/**
+	 * Called when a module is initialised
+	 *
+	 * @param {object} module - the module object/class
+	 */
 	onInitialize(module) {
 		if (this.modulesNotInitialized.indexOf(module) !== -1) {
 			this.modulesNotInitialized.splice(this.modulesNotInitialized.indexOf(module), 1);
@@ -316,12 +331,21 @@ class ModuleManager {
 		}
 	}
 
+	/**
+	 * Called when a module fails to initialise
+	 *
+	 * @param {object} module - the module object/class
+	 */
 	onFail(module) {
 		if (this.modulesNotInitialized.indexOf(module) !== -1) {
 			console.log("A module failed to initialize!");
 		}
 	}
 
+	/**
+	 * Called when every module has initialised
+	 *
+	 */
 	onAllModulesInitialized() {
 		console.log("All modules initialized!");
 		this.modules.discord.runJob("SEND_ADMIN_ALERT_MESSAGE", {

+ 15 - 1
backend/logic/activities.js

@@ -3,10 +3,16 @@ import async from "async";
 import CoreClass from "../core";
 
 class ActivitiesModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("activities");
 	}
 
+	/**
+	 * Initialises the activities module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise(resolve => {
 			this.db = this.moduleManager.modules.db;
@@ -18,8 +24,16 @@ class ActivitiesModule extends CoreClass {
 	}
 
 	// TODO: Migrate
+	/**
+	 * Adds a new activity to the database
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.userId - the id of the user who's activity is to be added
+	 * @param {string} payload.activityType - the type of activity (enum specified in schema)
+	 * @param {Array} payload.payload - the details of the activity e.g. an array of songs that were added
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	ADD_ACTIVITY(payload) {
-		// userId, activityType, payload
 		return new Promise((resolve, reject) => {
 			async.waterfall(
 				[

+ 6 - 0
backend/logic/api.js

@@ -5,10 +5,16 @@ import async from "async";
 import CoreClass from "../core";
 
 class APIModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("api");
 	}
 
+	/**
+	 * Initialises the api module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise((resolve, reject) => {
 			this.app = this.moduleManager.modules.app;

+ 16 - 0
backend/logic/app.js

@@ -13,10 +13,16 @@ import CoreClass from "../core";
 const { OAuth2 } = oauth;
 
 class AppModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("app");
 	}
 
+	/**
+	 * Initialises the app module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise(resolve => {
 			const { mail } = this.moduleManager.modules;
@@ -436,12 +442,22 @@ class AppModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns the express server
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	SERVER() {
 		return new Promise(resolve => {
 			resolve(this.server);
 		});
 	}
 
+	/**
+	 * Returns the app object
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_APP() {
 		return new Promise(resolve => {
 			resolve({ app: this.app });

+ 18 - 0
backend/logic/cache/index.js

@@ -10,10 +10,16 @@ const pubs = {};
 const subs = {};
 
 class CacheModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("cache");
 	}
 
+	/**
+	 * Initialises the cache/redis module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		const importSchema = schemaName =>
 			new Promise(resolve => {
@@ -71,6 +77,11 @@ class CacheModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Quits redis client
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	QUIT() {
 		return new Promise(resolve => {
 			if (this.client.connected) {
@@ -255,6 +266,13 @@ class CacheModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns a redis schema
+	 *
+	 * @param {object} payload - object containing the payload
+	 * @param {string} payload.schemaName - the name of the schema to get
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_SCHEMA(payload) {
 		return new Promise(resolve => {
 			resolve(this.schemas[payload.schemaName]);

+ 26 - 0
backend/logic/db/index.js

@@ -17,10 +17,16 @@ const isLength = (string, min, max) => !(typeof string !== "string" || string.le
 mongoose.Promise = bluebird;
 
 class DBModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("db");
 	}
 
+	/**
+	 * Initialises the database module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise((resolve, reject) => {
 			this.schemas = {};
@@ -256,18 +262,38 @@ class DBModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns a database model
+	 *
+	 * @param {object} payload - object containing the payload
+	 * @param {object} payload.modelName - name of the model to get
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_MODEL(payload) {
 		return new Promise(resolve => {
 			resolve(this.models[payload.modelName]);
 		});
 	}
 
+	/**
+	 * Returns a database schema
+	 *
+	 * @param {object} payload - object containing the payload
+	 * @param {object} payload.schemaName - name of the schema to get
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_SCHEMA(payload) {
 		return new Promise(resolve => {
 			resolve(this.schemas[payload.schemaName]);
 		});
 	}
 
+	/**
+	 * Checks if a password to be stored in the database has a valid length
+	 *
+	 * @param {object} password - the password itself
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	passwordValid(password) {
 		return isLength(password, 6, 200);
 	}

+ 18 - 0
backend/logic/discord.js

@@ -4,10 +4,16 @@ import Discord from "discord.js";
 import CoreClass from "../core";
 
 class DiscordModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("discord");
 	}
 
+	/**
+	 * Initialises the discord module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise((resolve, reject) => {
 			this.log("INFO", "Discord initialize");
@@ -48,9 +54,21 @@ class DiscordModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Adds a new activity to the database
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.color - The colour of the alert title
+	 * @param {string} payload.message - The message to send as the alert
+	 * @param {string} payload.type - The type of alert e.g. Startup
+	 * @param {boolean} payload.critical - If the message is service critical
+	 * @param {Array} payload.extraFields - Any extra fields to show in the discord message
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	SEND_ADMIN_ALERT_MESSAGE(payload) {
 		return new Promise((resolve, reject) => {
 			const channel = this.client.channels.find(channel => channel.id === this.adminAlertChannelId);
+
 			if (channel !== null) {
 				const richEmbed = new Discord.RichEmbed();
 				richEmbed.setAuthor(

+ 11 - 0
backend/logic/io.js

@@ -10,10 +10,16 @@ import actions from "./actions";
 import CoreClass from "../core";
 
 class IOModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("io");
 	}
 
+	/**
+	 * Initialises the io module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		this.setStage(1);
 
@@ -308,6 +314,11 @@ class IOModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns the socket io variable
+	 *
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	IO() {
 		return new Promise(resolve => {
 			resolve(this._io);

+ 20 - 1
backend/logic/mail/index.js

@@ -5,10 +5,16 @@ import Mailgun from "mailgun-js";
 import CoreClass from "../../core";
 
 class MailModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("mail");
 	}
 
+	/**
+	 * Initialises the mail module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		const importSchema = schemaName =>
 			new Promise(resolve => {
@@ -32,8 +38,14 @@ class MailModule extends CoreClass {
 		return new Promise(resolve => resolve());
 	}
 
+	/**
+	 * Sends an email
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.data - information such as to, from in order to send the email
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	SEND_MAIL(payload) {
-		// data
 		return new Promise(resolve => {
 			if (this.enabled)
 				this.mailgun.messages().send(payload.data, () => {
@@ -43,6 +55,13 @@ class MailModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns an email schema
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.schemaName - name of the schema to get
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_SCHEMA(payload) {
 		return new Promise(resolve => {
 			resolve(this.schemas[payload.schemaName]);

+ 15 - 2
backend/logic/notifications.js

@@ -7,12 +7,18 @@ import CoreClass from "../core";
 import utils from "./utils";
 
 class NotificationsModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("notifications");
 
 		this.subscriptions = [];
 	}
 
+	/**
+	 * Initialises the notifications module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise((resolve, reject) => {
 			const url = (this.url = config.get("redis").url);
@@ -147,10 +153,10 @@ class NotificationsModule extends CoreClass {
 	 * @param {object} payload - object containing the payload
 	 * @param {string} payload.name - the name of the notification we want to schedule
 	 * @param {number} payload.time - how long in milliseconds until the notification should be fired
+	 * @param {object} payload.station - the station object related to the notification
 	 * @returns {Promise} - returns a promise (resolve, reject)
 	 */
 	SCHEDULE(payload) {
-		// name, time, cb, station
 		return new Promise((resolve, reject) => {
 			const time = Math.round(payload.time);
 			this.log(
@@ -180,10 +186,10 @@ class NotificationsModule extends CoreClass {
 	 * @param {object} payload - object containing the payload
 	 * @param {string} payload.name - the name of the notification we want to subscribe to
 	 * @param {boolean} payload.unique - only subscribe if another subscription with the same name doesn't already exist
+	 * @param {object} payload.station - the station object related to the notification
 	 * @returns {Promise} - returns a promise (resolve, reject)
 	 */
 	SUBSCRIBE(payload) {
-		// name, cb, unique = false, station
 		return new Promise(resolve => {
 			this.log(
 				"STATION_ISSUE",
@@ -229,6 +235,13 @@ class NotificationsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Unschedules a notification by name (each notification has a unique name)
+	 *
+	 * @param {object} payload - object containing the payload
+	 * @param {string} payload.name - the name of the notification we want to schedule
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	UNSCHEDULE(payload) {
 		// name
 		return new Promise((resolve, reject) => {

+ 6 - 0
backend/logic/playlists.js

@@ -3,10 +3,16 @@ import async from "async";
 import CoreClass from "../core";
 
 class PlaylistsModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("playlists");
 	}
 
+	/**
+	 * Initialises the playlists module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		this.setStage(1);
 

+ 12 - 5
backend/logic/punishments.js

@@ -3,10 +3,16 @@ import mongoose from "mongoose";
 import CoreClass from "../core";
 
 class PunishmentsModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("punishments");
 	}
 
+	/**
+	 * Initialises the punishments module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		this.setStage(1);
 
@@ -166,7 +172,6 @@ class PunishmentsModule extends CoreClass {
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	GET_PUNISHMENT(payload) {
-		// id, cb
 		return new Promise((resolve, reject) => {
 			let punishmentModel;
 
@@ -226,7 +231,6 @@ class PunishmentsModule extends CoreClass {
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	GET_PUNISHMENTS_FROM_USER_ID(payload) {
-		// userId, cb
 		return new Promise((resolve, reject) => {
 			async.waterfall(
 				[
@@ -253,14 +257,17 @@ class PunishmentsModule extends CoreClass {
 	}
 
 	/**
-	 * Gets all punishments from a userId
+	 * Adds a new punishment to the database
 	 *
 	 * @param {object} payload - object containing the payload
-	 * @param {string} payload.userId - the userId of the punishment(s) we are trying to get
+	 * @param {string} payload.reason - the reason for the punishment e.g. spam
+	 * @param {string} payload.type - the type of punishment (enum: ["banUserId", "banUserIp"])
+	 * @param {string} payload.value - the user id/ip address for the ban (depends on punishment type)
+	 * @param {Date} payload.expiresAt - the date at which the punishment expires at
+	 * @param {string} payload.punishedBy - the userId of the who initiated the punishment
 	 * @returns {Promise} - returns promise (reject, resolve)
 	 */
 	ADD_PUNISHMENT(payload) {
-		// type, value, reason, expiresAt, punishedBy, cb
 		return new Promise((resolve, reject) => {
 			let PunishmentModel;
 			let PunishmentSchema;

+ 6 - 0
backend/logic/songs.js

@@ -3,10 +3,16 @@ import mongoose from "mongoose";
 import CoreClass from "../core";
 
 class SongsModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("songs");
 	}
 
+	/**
+	 * Initialises the songs module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		this.setStage(1);
 

+ 16 - 0
backend/logic/spotify.js

@@ -15,10 +15,16 @@ let apiResults = {
 };
 
 class SpotifyModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("spotify");
 	}
 
+	/**
+	 * Initialises the spotify module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise((resolve, reject) => {
 			this.cache = this.moduleManager.modules.cache;
@@ -61,6 +67,11 @@ class SpotifyModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns the request token for the Spotify api if one exists, otherwise creates a new one
+	 *
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	GET_TOKEN() {
 		return new Promise(resolve => {
 			if (Date.now() > apiResults.expires_at) {
@@ -71,6 +82,11 @@ class SpotifyModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Creates a request token for the Spotify api
+	 *
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	REQUEST_TOKEN() {
 		return new Promise(resolve => {
 			async.waterfall(

+ 70 - 9
backend/logic/stations.js

@@ -3,10 +3,16 @@ import async from "async";
 import CoreClass from "../core";
 
 class StationsModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("stations");
 	}
 
+	/**
+	 * Initialises the stations module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async initialize() {
 		this.cache = this.moduleManager.modules.cache;
 		this.db = this.moduleManager.modules.db;
@@ -180,8 +186,15 @@ class StationsModule extends CoreClass {
 		);
 	}
 
+	/**
+	 * Initialises a station
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.stationId - id of the station to initialise
+	 * @param {boolean} payload.bypassQueue - UNKNOWN
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	INITIALIZE_STATION(payload) {
-		// stationId, cb, bypassValidate = false
 		return new Promise((resolve, reject) => {
 			// if (typeof cb !== 'function') cb = ()=>{};
 
@@ -286,6 +299,14 @@ class StationsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Calculates the next song for the station
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.station - station object to calculate song for
+	 * @param {boolean} payload.bypassQueue - UNKNOWN
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	async CALCULATE_SONG_FOR_STATION(payload) {
 		// station, bypassValidate = false
 		const stationModel = await this.db.runJob("GET_MODEL", { modelName: "station" });
@@ -388,9 +409,14 @@ class StationsModule extends CoreClass {
 		});
 	}
 
-	// Attempts to get the station from Redis. If it's not in Redis, get it from Mongo and add it to Redis.
+	/**
+	 * Attempts to get the station from Redis. If it's not in Redis, get it from Mongo and add it to Redis.
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.stationId - id of the station
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	GET_STATION(payload) {
-		// stationId, cb, bypassValidate = false
 		return new Promise((resolve, reject) => {
 			async.waterfall(
 				[
@@ -446,9 +472,14 @@ class StationsModule extends CoreClass {
 		});
 	}
 
-	// Attempts to get the station from Redis. If it's not in Redis, get it from Mongo and add it to Redis.
+	/**
+	 * Attempts to get a station by name, firstly from Redis. If it's not in Redis, get it from Mongo and add it to Redis.
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.stationName - the unique name of the station
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	async GET_STATION_BY_NAME(payload) {
-		// stationName
 		const stationModel = await this.db.runJob("GET_MODEL", { modelName: "station" });
 
 		return new Promise((resolve, reject) =>
@@ -486,8 +517,14 @@ class StationsModule extends CoreClass {
 		);
 	}
 
+	/**
+	 * Updates the station in cache from mongo or deletes station in cache if no longer in mongo.
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.stationId - the id of the station to update
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	UPDATE_STATION(payload) {
-		// stationId, cb, bypassValidate = false
 		return new Promise((resolve, reject) => {
 			async.waterfall(
 				[
@@ -531,10 +568,19 @@ class StationsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Creates the official playlist for a station
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.stationId - the id of the station
+	 * @param {Array} payload.songList - list of songs to put in official playlist
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	async CALCULATE_OFFICIAL_PLAYLIST_LIST(payload) {
-		// stationId, songList, cb, bypassValidate = false
 		const officialPlaylistSchema = await this.cache.runJob("GET_SCHEMA", { schemaName: "officialPlaylist" });
 
+		console.log(typeof payload.songList, payload.songList);
+
 		return new Promise(resolve => {
 			const lessInfoPlaylist = [];
 
@@ -578,8 +624,15 @@ class StationsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Skips a station
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.stationId - the id of the station to skip
+	 * @param {boolean} payload.bypassQueue - UNKNOWN
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	SKIP_STATION(payload) {
-		// stationId
 		return new Promise((resolve, reject) => {
 			this.log("INFO", `Skipping station ${payload.stationId}.`);
 
@@ -920,8 +973,16 @@ class StationsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Checks if a user can view/access a station
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.station - the station object of the station in question
+	 * @param {string} payload.userId - the id of the user in question
+	 * @param {boolean} payload.hideUnlisted - whether the user is allowed to see unlisted stations or not
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	CAN_USER_VIEW_STATION(payload) {
-		// station, userId, hideUnlisted
 		return new Promise((resolve, reject) => {
 			async.waterfall(
 				[

+ 52 - 0
backend/logic/tasks.js

@@ -9,12 +9,18 @@ import Timer from "../classes/Timer.class";
 const __dirname = path.dirname(fileURLToPath(import.meta.url));
 
 class TasksModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("tasks");
 
 		this.tasks = {};
 	}
 
+	/**
+	 * Initialises the tasks module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise(resolve => {
 			// return reject(new Error("Not fully migrated yet."));
@@ -48,6 +54,16 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Creates a new task
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.name - the name of the task
+	 * @param {string} payload.fn - the function the task will run
+	 * @param {string} payload.paused - if the task is currently paused
+	 * @param {boolean} payload.timeout - how often to run the task
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	CREATE_TASK(payload) {
 		return new Promise((resolve, reject) => {
 			this.tasks[payload.name] = {
@@ -66,6 +82,13 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Pauses a task
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.taskName - the name of the task to pause
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	PAUSE_TASK(payload) {
 		const taskName = { payload };
 
@@ -75,6 +98,13 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Resumes a task
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.name - the name of the task to resume
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	RESUME_TASK(payload) {
 		return new Promise(resolve => {
 			this.tasks[payload.name].timer.resume();
@@ -82,6 +112,13 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Runs a task's function and restarts the timer
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.name - the name of the task to run
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	RUN_TASK(payload) {
 		return new Promise(resolve => {
 			const task = this.tasks[payload.name];
@@ -95,6 +132,11 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Periodically checks if any stations need to be skipped
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	checkStationSkipTask() {
 		return new Promise(resolve => {
 			this.log("INFO", "TASK_STATIONS_SKIP_CHECK", `Checking for stations to be skipped.`, false);
@@ -135,6 +177,11 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Periodically checks if any sessions are out of date and need to be cleared
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	sessionClearingTask() {
 		return new Promise(resolve => {
 			this.log("INFO", "TASK_SESSION_CLEAR", `Checking for sessions to be cleared.`);
@@ -229,6 +276,11 @@ class TasksModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Periodically warns about the size of any log files
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	logFileSizeCheckTask() {
 		return new Promise((resolve, reject) => {
 			this.log("INFO", "TASK_LOG_FILE_SIZE_CHECK", `Checking the size for the log files.`);

+ 196 - 23
backend/logic/utils.js

@@ -6,6 +6,7 @@ import request from "request";
 import CoreClass from "../core";
 
 class UtilsModule extends CoreClass {
+	// eslint-disable-next-line require-jsdoc
 	constructor() {
 		super("utils");
 
@@ -14,6 +15,11 @@ class UtilsModule extends CoreClass {
 		this.youtubeRequestsActive = false;
 	}
 
+	/**
+	 * Initialises the utils module
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	initialize() {
 		return new Promise(resolve => {
 			this.io = this.moduleManager.modules.io;
@@ -25,8 +31,14 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Parses the cookie into a readable object
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.cookieString - the cookie string
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	PARSE_COOKIES(payload) {
-		// cookieString
 		return new Promise((resolve, reject) => {
 			const cookies = {};
 
@@ -54,8 +66,15 @@ class UtilsModule extends CoreClass {
 	//     });
 	// }
 
+	/**
+	 * Removes a cookie by name
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.cookieString - the cookie string
+	 * @param {string} payload.cookieName - the unique name of the cookie
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	REMOVE_COOKIE(payload) {
-		// cookieString, cookieName
 		return new Promise((resolve, reject) => {
 			let cookies;
 
@@ -73,8 +92,14 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Replaces any html reserved characters in a string with html entities
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.str - the string to replace characters with html entities
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	HTML_ENTITIES(payload) {
-		// str
 		return new Promise(resolve => {
 			resolve(
 				String(payload.str)
@@ -86,9 +111,14 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Generates a random string of a specified length
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {number} payload.length - the length the random string should be
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async GENERATE_RANDOM_STRING(payload) {
-		// length
-
 		const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split("");
 
 		const promises = [];
@@ -111,6 +141,13 @@ class UtilsModule extends CoreClass {
 		return new Promise(resolve => resolve(randomChars.join("")));
 	}
 
+	/**
+	 * Returns a socket object from a socket identifier
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.socketId - the socket id
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async GET_SOCKET_FROM_ID(payload) {
 		// socketId
 		const io = await this.io.runJob("IO", {});
@@ -118,6 +155,14 @@ class UtilsModule extends CoreClass {
 		return new Promise(resolve => resolve(io.sockets.sockets[payload.socketId]));
 	}
 
+	/**
+	 * Creates a random number within a range
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {number} payload.min - the minimum number the result should be
+	 * @param {number} payload.max - the maximum number the result should be
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_RANDOM_NUMBER(payload) {
 		// min, max
 		return new Promise(resolve =>
@@ -125,6 +170,13 @@ class UtilsModule extends CoreClass {
 		);
 	}
 
+	/**
+	 * Converts ISO8601 time format (YouTube API) to HH:MM:SS
+	 *
+	 * @param  {object} payload - object contaiing the payload
+	 * @param {string} payload.duration - string in the format of ISO8601
+	 * @returns {Promise} - returns a promise (resolve, reject)
+	 */
 	CONVERT_TIME(payload) {
 		// duration
 		return new Promise(resolve => {
@@ -171,6 +223,11 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Creates a random identifier for e.g. sessionId
+	 *
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GUID() {
 		return new Promise(resolve => {
 			resolve(
@@ -187,6 +244,8 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	// UNKNOWN
+	// eslint-disable-next-line require-jsdoc
 	async SOCKET_FROM_SESSION(payload) {
 		// socketId
 		const io = await this.io.runJob("IO", {});
@@ -201,6 +260,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Gets all sockets for a specified session id
+	 *
+	 * @param {object} payload - object containing the payload
+	 * @param {string} payload.sessionId - user session id
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async SOCKETS_FROM_SESSION_ID(payload) {
 		const io = await this.io.runJob("IO", {});
 
@@ -226,6 +292,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns any sockets for a specific user
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.userId - the user id
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async SOCKETS_FROM_USER(payload) {
 		const io = await this.io.runJob("IO", {});
 
@@ -262,6 +335,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns any sockets from a specific ip address
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.ip - the ip address in question
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async SOCKETS_FROM_IP(payload) {
 		const io = await this.io.runJob("IO", {});
 
@@ -294,6 +374,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns any sockets from a specific user without using redis/cache
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.userId - the id of the user in question
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async SOCKETS_FROM_USER_WITHOUT_CACHE(payload) {
 		const io = await this.io.runJob("IO", {});
 
@@ -319,19 +406,19 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
-	SOCKET_LEAVE_ROOMS(payload) {
-		// socketId
-		return new Promise((resolve, reject) => {
-			let socket;
-
-			try {
-				socket = this.runJob("SOCKET_FROM_SESSION", {
-					socketId: payload.socketId
-				});
-			} catch (err) {
-				return reject(err);
-			}
+	/**
+	 * Allows a socket to leave any rooms they are connected to
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.socketId - the id of the socket which should leave all their rooms
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
+	async SOCKET_LEAVE_ROOMS(payload) {
+		const socket = await this.runJob("SOCKET_FROM_SESSION", {
+			socketId: payload.socketId
+		});
 
+		return new Promise(resolve => {
 			const { rooms } = socket;
 			for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
 				socket.leave(room);
@@ -341,12 +428,19 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Allows a socket to join a specified room
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.socketId - the id of the socket which should join the room
+	 * @param {object} payload.room - the object representing the room the socket should join
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async SOCKET_JOIN_ROOM(payload) {
 		const socket = await this.runJob("SOCKET_FROM_SESSION", {
 			socketId: payload.socketId
 		});
 
-		// socketId, room
 		return new Promise(resolve => {
 			const { rooms } = socket;
 			for (let room = 0, roomKeys = Object.keys(rooms); room < roomKeys.length; room += 1) {
@@ -359,6 +453,8 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	// UNKNOWN
+	// eslint-disable-next-line require-jsdoc
 	async SOCKET_JOIN_SONG_ROOM(payload) {
 		// socketId, room
 		const socket = await this.runJob("SOCKET_FROM_SESSION", {
@@ -377,6 +473,8 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	// UNKNOWN
+	// eslint-disable-next-line require-jsdoc
 	SOCKETS_JOIN_SONG_ROOM(payload) {
 		// sockets, room
 		return new Promise(resolve => {
@@ -395,6 +493,8 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	// UNKNOWN
+	// eslint-disable-next-line require-jsdoc
 	SOCKETS_LEAVE_SONG_ROOMS(payload) {
 		// sockets
 		return new Promise(resolve => {
@@ -409,8 +509,15 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Emits arguments to any sockets that are in a specified a room
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.room - the name of the room to emit arguments
+	 * @param {object} payload.args - any arguments to be emitted to the sockets in the specific room
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async EMIT_TO_ROOM(payload) {
-		// room, ...args
 		const io = await this.io.runJob("IO", {});
 
 		return new Promise(resolve => {
@@ -426,10 +533,16 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Gets any sockets connected to a room
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.room - the name of the room
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async GET_ROOM_SOCKETS(payload) {
 		const io = await this.io.runJob("IO", {});
 
-		// room
 		return new Promise(resolve => {
 			const { sockets } = io.sockets;
 			const roomSockets = [];
@@ -442,6 +555,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Gets the details of a song using the YouTube API
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.songId - the YouTube API id of the song
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_SONG_FROM_YOUTUBE(payload) {
 		// songId, cb
 		return new Promise((resolve, reject) => {
@@ -521,6 +641,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Filters a list of YouTube videos so that they only contains videos with music
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {Array} payload.videoIds - an array of YouTube videoIds to filter through
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	FILTER_MUSIC_VIDEOS_YOUTUBE(payload) {
 		// videoIds, cb
 		return new Promise((resolve, reject) => {
@@ -574,6 +701,14 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns an array of songs taken from a YouTube playlist
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {boolean} payload.musicOnly - whether to return music videos or all videos in the playlist
+	 * @param {string} payload.url - the url of the YouTube playlist
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_PLAYLIST_FROM_YOUTUBE(payload) {
 		// payload includes: url, musicOnly
 		return new Promise((resolve, reject) => {
@@ -593,7 +728,7 @@ class UtilsModule extends CoreClass {
 
 			/**
 			 * @param {string} pageToken - page token for YouTube API
-			 * @param {Array} songs - array of sogns
+			 * @param {Array} songs - array of songs
 			 */
 			function getPage(pageToken, songs) {
 				const nextPageToken = pageToken ? `pageToken=${pageToken}` : "";
@@ -642,6 +777,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Gets the details of a song from the Spotify API
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.song - the song object (song.title etc.)
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async GET_SONG_FROM_SPOTIFY(payload) {
 		// song
 		const token = await this.spotify.runJob("GET_TOKEN", {});
@@ -689,6 +831,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Returns the details of multiple songs from the Spotify API
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.title - the query/title of a song to search the API with
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	async GET_SONGS_FROM_SPOTIFY(payload) {
 		// title, artist
 		const token = await this.spotify.runJob("GET_TOKEN", {});
@@ -741,6 +890,13 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Shuffles an array of songs
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.array - an array of songs that should be shuffled
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	SHUFFLE(payload) {
 		// array
 		return new Promise(resolve => {
@@ -766,8 +922,16 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Creates an error
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {object} payload.error - object that contains the error
+	 * @param {string} payload.message - possible error message
+	 * @param {object} payload.errors - possible object that contains multiple errors
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	GET_ERROR(payload) {
-		// err
 		return new Promise(resolve => {
 			let error = "An error occurred.";
 			if (typeof payload.error === "string") error = payload.error;
@@ -779,8 +943,14 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * Creates the gravatar url for a specified email address
+	 *
+	 * @param {object} payload - object that contains the payload
+	 * @param {string} payload.email - the email address
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	CREATE_GRAVATAR(payload) {
-		// email
 		return new Promise(resolve => {
 			const hash = crypto.createHash("md5").update(payload.email).digest("hex");
 
@@ -788,6 +958,9 @@ class UtilsModule extends CoreClass {
 		});
 	}
 
+	/**
+	 * @returns {Promise} - returns promise (reject, resolve)
+	 */
 	DEBUG() {
 		return new Promise(resolve => resolve());
 	}