Files
Emmelia-Link-Flayer-Rewrite/libStorage.js
2023-02-24 21:27:55 -05:00

299 lines
11 KiB
JavaScript

// Customizable storage module for any mode of storage
// Update the functions here to change the storage medium
// Import modules
const { DebugBuilder } = require("./utilities/debugBuilder");
const log = new DebugBuilder("server", "libStorage");
// Storage Specific Modules
// MySQL
const mysql = require("mysql");
// Helper Functions
// Function to run and handle SQL errors
function runSQL(sqlQuery, connection, callback = (err, rows) => {
log.ERROR(err);
throw err;
}) {
// Start the MySQL Connection
//connection.connect();
connection.query(sqlQuery, (err, rows) => {
if (err) {
log.ERROR("SQL Error:", err)
callback(err, undefined);
}
log.DEBUG("RunSQL Returned Rows:", rows);
callback(undefined, rows);
})
//connection.
}
class RSSRecord {
/**
*
* @param {*} _id
* @param {*} _title
* @param {*} _link
* @param {*} _category
*/
constructor(_id, _title, _link, _category) {
this.id = _id;
this.title = _title;
this.link= _link;
this.category = _category;
}
getId() {
return this.id;
}
get(key) {
if (!Object.keys(this).includes(key)) throw new Error("Key is invalid", key);
return this[key]
}
}
exports.RSSRecord = RSSRecord;
exports.Storage = class Storage {
constructor(_dbTable) {
this.connection = mysql.createPool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME
});
// Set the DB Table for later use
this.dbTable = _dbTable
}
/**
* Wrapper to save a new entry using the storage method configured
* @param {Array} toBeSaved Entry or Entries to be added
* @param {function} callback The callback function to be called with the record when saved
*/
create(toBeSaved, callback) {
log.DEBUG("To be saved:", toBeSaved);
log.DEBUG("to be saved length:", toBeSaved.length);
if (!toBeSaved[0].fields?.title) callback(Error("No title given"), undefined);
let newRecords = []
for (var entry of toBeSaved) {
entry = entry.fields
log.DEBUG("Entry:", entry);
this.returnRecord(undefined, entry.title, entry.link, entry.category, (err, record) => {
if (err) callback(err, undefined);
newRecords.push(record);
if (toBeSaved.length === 1) {
log.DEBUG("One record to callback with:", record);
callback(undefined, record);
}
})
}
if (!toBeSaved.length === 1) {
callback(undefined, newRecords);
}
}
/**
* 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);
this.getRecordBy('id', entryID, (err, entryRecord) => {
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|*}
*/
checkForTitle(title, callback) {
if (!title) callback(new Error("No title given when checking for title"), undefined);
const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE title = '${title}'`;
runSQL(sqlQuery, this.connection, (err, rows) => {
if (err) callback(err, undefined);
if (rows[0]?.title) callback(undefined, true);
else callback(undefined, 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, callback) {
const validKeys = ["link", "title", "category", "id"];
if (!validKeys.includes(key)) callback(new Error("Given key not valid"), undefined);
const sqlQuery = `SELECT * FROM ${this.dbTable} WHERE ${key} = '${keyValue}'`;
runSQL(sqlQuery, this.connection, (err, rows) => {
if (err) callback(err, undefined);
if (rows[0]?.title) callback(undefined, rows[0]);
else callback(undefined, 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) {
log.DEBUG("Saving entry:", entryObject);
if (!entryObject?.title || !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.title}', '${entryObject.link}', '${entryObject.category}');`;
log.DEBUG(`Adding new entry with SQL query: '${sqlQuery}'`)
runSQL(sqlQuery, this.connection, (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}';`
log.DEBUG(`Updating entry with SQL query: '${sqlQuery}'`)
runSQL(sqlQuery, this.connection, (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(sqlQuery, this.connection, (err, rows) => {
if (err) callback(err, undefined);
callback(undefined, rows[0]);
})
}
/**
* Returns a record class for the given information, if there's no ID, it will create it
* @param {*} _id The ID / line number of the record in the storage medium (OPT)
* @param {*} _title The title of the record
* @param {*} _link The link to the RSS feed
* @param {*} _category The category of the record
* @param {*} callback Callback function to return an error or the record
*/
returnRecord(_id, _title, _link, _category, callback) {
log.DEBUG(`Return record for these values: ID: '${_id}', Title: '${_title}', Category: '${_category}', Link: '${_link}'`)
if (!_link && !_title) callback(new Error("No link or title given when creating a record"), undefined);
let entryObject = {
"title": _title,
"link": _link
}
if (_category) entryObject.category = _category;
if (_id) {
entryObject.id = _id;
this.updateEntry(entryObject, (err, rows) => {
if (err) callback(err, undefined);
this.getRecordBy('id', entryObject.id, (err, record) => {
if (err) callback(err, undefined);
callback(undefined, new RSSRecord(record.id, record.title, record.link, record.category));
})
})
}
else {
this.checkForTitle(_title, (err, titleExists) => {
if (!titleExists) {
log.DEBUG("Entry doesn't exist, making one now", entryObject);
this.saveEntry(entryObject, (err, rows) => {
if (err) callback(err, undefined);
this.getRecordBy("title", entryObject.title, (err, record) => {
if (err) callback(err, undefined);
callback(undefined, new RSSRecord(record.id, record.title, record.link, record.category));
})
});
}
else{
this.updateEntry(entryObject, (err, rows) => {
if (err) callback(err, undefined);
this.getRecordBy('title', entryObject.title, (err, record) => {
if (err) callback(err, undefined);
callback(undefined, new RSSRecord(record.id, record.title, record.link, record.category));
})
})
}
})
}
}
/**
* Get all records stored
* @param {function} callback
*/
getAllRecords(callback) {
log.INFO("Getting all records");
const sqlQuery = `SELECT * FROM ${this.dbTable}`
let rssRecords = [];
runSQL(sqlQuery, this.connection, (err, rows) => {
if (err) callback(err, undefined);
for (const row of rows) {
log.DEBUG("Row from SQL query:", row);
rssRecords.push(new RSSRecord(row.id, row.title, row.link, row.category));
}
log.DEBUG("All records:", rssRecords);
callback(undefined, rssRecords);
});
}
}