// Customizable storage module for any mode of storage // Update the functions here to change the storage medium // Import modules // Storage Specific Modules // MySQL const e = require("express"); const mysql = require("mysql"); // Helper Functions // Function to run and handle SQL errors function runSQL(sqlQuery, connection, callback = (err, rows) => { console.log(err); throw err; }) { connection.query(sqlQuery, (err, rows) => { if (err) return callback(err, undefined); //console.log('The rows are:', rows); return callback(undefined, rows); }) } exports.Storage = class Storage { constructor(_dbTable) { this.connection = mysql.createConnection({ host: process.env.DB_HOST, user: databaseConfig.DB_USER, password: databaseConfig.DB_PASS, database: databaseConfig.DB_NAME }); // Start the MySQL Connection this.connection.connect(); // Set the DB Table for later use this.dbTable = _dbTable } /** * Wrapper to save a new entry using the storage method configured * @param {*} toBeSaved Entry or Entries to be added * @param {function} callback The callback function to be called with the record when saved */ create(toBeSaved, callback) { if (!toBeSaved[0].fields?.title) callback(Error("No title given"), undefined); for (const entry of toBeSaved) { if(!this.checkForTitleSync(entry.title)) { this.saveEntry(entry, (err, rows) => { if (err) callback(err, undefined); if (rows?.affectedRows) callback(undefined, rows[0]); }) } } } /** * Wrapper to delete an entry using the storage method configured * @param {} entryID The ID of the entry to be deleted * @param {function} callback The callback function to be called with the record when deleted */ destroy(entryID, callback) { if (!entryID) callback(Error("No entry ID given"), undefined); const entryRecord = this.getRecordBy('id', entryID); this.removeEntry(entryRecord.id, (err, results) => { if (err) callback(err, undefined); callback(undefined, results); }); } /** * Check to see if an entry exists in the storage method configured * @param {*} title The title of the entry to check if it exists * @returns {true|false|*} */ checkForTitleSync(title) { if (!title) throw new Error("No title given when checking for title"); const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE title = ${title}`; runSQL(this.connection, sqlQuery, (err, rows) => { if (err) throw err; if (rows[0]?.title) return true; else return false; }) } /** * Get a record by a specified key * @param {*} key The key to search for * @param {*} keyValue The value of the key to search for */ getRecordBy(key, keyValue) { const validKeys = ["link", "title", "category", "id"]; if (!Array(validKeys).includes(key)) throw new Error("Given key not valid"); const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE ${key} = ${keyValue}`; runSQL(this.connection, sqlQuery, (err, rows) => { if (err) throw err; if (rows[0]?.title) return rows[0]; else return false; }) } /** * Save the given entry to the storage medium * @param {Object} entryObject The entry object to be saved * @param {function} callback The callback to be called with either an error or undefined if successful */ saveEntry(entryObject, callback) { if (!entryObject?.table || entryObject?.link || entryObject?.category) { callback(new Error("Entry object malformed, check the object before saving it"), undefined) } const sqlQuery = `INSERT INTO ${this.dbTable} (title, link, category) VALUES ('${entryObject.table}', '${entryObject.link}', '${entryObject.category}')`; runSQL(this.connection, sqlQuery, (err, rows) => { if (err) callback(err, undefined); callback(undefined, rows); }) } /** * Save the given entry to the storage medium * @param {Object} entryObject The entry object to be saved * @param {function} callback The callback to be called with either an error or undefined if successful */ updateEntry(entryObject, callback) { let queryParams = []; if (!entryObject.title) callback(new Error("No title given before updating"), undefined); queryParams.push(`title = '${entryObject.title}`); if (!entryObject.link) callback(new Error("No link given before updating"), undefined); queryParams.push(`link = '${entryObject.link}`); if (entryObject.category) queryParams.push(`category = '${entryObject.category}`); let sqlQuery = `UPDATE ${this.dbTable} SET`; let i = 0; for (const param of queryParams) { if (i === queryParams.length-1) { sqlQuery = `${sqlQuery} ${param}` i += 1; } else { sqlQuery = `${sqlQuery} ${param},` i += 1; } } sqlQuery = `${sqlQuery} WHERE title = '${entryObject.title}';` runSQL(this.connection, sqlQuery, (err, rows) => { if (err) callback(err, undefined); callback(undefined, rows); }) } /** * Delete the given entry from the storage medium * @param {string} title The title of the entry to be deleted * @param {function} callback The callback to be called with either an error or undefined if successful */ removeEntry(title, callback) { if (!title) { callback(new Error("No entry title given before deleting"), undefined) } const sqlQuery = `DELETE FROM ${this.dbTable} WHERE title = '${title}'`; runSQL(this.connection, sqlQuery, (err, rows) => { if (err) callback(err, undefined); callback(undefined, rows); }) } }