From bc597b2ccb79f98e11df0242745b612000df2807 Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Fri, 16 Jul 2021 03:14:28 -0700 Subject: [PATCH] define sqldatabase prototype This will allow us to define a sqllite module later, which we'll want to transition to in order to do away with document data storage as well as using a full blown mysql database for a simple scanner frontend. --- js/api.js | 2 +- js/database.js | 59 +++++++++++++----------- js/mysql.js | 46 ++++++++++--------- js/output.js | 4 +- js/prototypes/baseproto.js | 14 ++++++ js/prototypes/sqldatabase.js | 89 ++++++++++++++++++++++++++++++++++++ js/scanner.js | 2 +- js/tests/utilities.js | 2 +- 8 files changed, 165 insertions(+), 53 deletions(-) create mode 100644 js/prototypes/baseproto.js create mode 100644 js/prototypes/sqldatabase.js diff --git a/js/api.js b/js/api.js index 0d655f8..b8d66db 100755 --- a/js/api.js +++ b/js/api.js @@ -173,7 +173,7 @@ var api = { handle_attendance_req: function(mysql, api, data) { console.log('API', 'SYNC', 'ATTENDANCE', 'Attendance data requested by API server. Sending...'); - mysql.connect().query('SELECT * FROM `attendance`', function(err, rows) { + mysql.query('SELECT * FROM `attendance`', function(err, rows) { if(err) { return console.log('MYSQL', 'SYNC', 'ATTENDANCE', 'ERR', err); diff --git a/js/database.js b/js/database.js index 359a1ea..20d787f 100755 --- a/js/database.js +++ b/js/database.js @@ -5,6 +5,11 @@ var consts = require('./constants.js'); var date = require('./date.js'); +var sqldatabase = require('./prototypes/sqldatabase.js'); + +const SqlServerExportStrategyName = 'mysql'; +const ExcelExportStrategyName = 'excel'; + var database = { @@ -362,10 +367,7 @@ var database = { console.log('MYSQL', 'Initializing event data...'); // create mysql entry for current event if it doesn't exist - mysql.connect() - - // insert new entry into `events` table - .query('INSERT IGNORE INTO `events` (table_name, event_name, semester, year) VALUES ("' + scanner.getEventId() + '", "' + scanner.getEventName() + '", "' + date.get_semester() + '", "' + date.get_year() + '")', function(err) { + mysql.query('INSERT IGNORE INTO `events` (table_name, event_name, semester, year) VALUES ("' + scanner.getEventId() + '", "' + scanner.getEventName() + '", "' + date.get_semester() + '", "' + date.get_year() + '")', function(err) { // tell program request has been parsed mysql.isBusy = false; @@ -380,8 +382,7 @@ var database = { // index event's table and see which entries from database exist on it (done in case application is restarted more than once in the same event) // update local database's entries with data from mysql table's entries - mysql.connect() - .query('SELECT * FROM `attendance` WHERE event_id="' + scanner.getEventId() + '"', function(err, evtRows, evtCols) { + mysql.query('SELECT * FROM `attendance` WHERE event_id="' + scanner.getEventId() + '"', function(err, evtRows, evtCols) { if(err) { return console.log('MYSQL', 'QUERY', 'An error occurred attempting to check previously stored data in mysql event table -> ' + err); @@ -463,15 +464,19 @@ var database = { }); }, - init: function(scanner, mysql, api, output) { + init: function(scanner, sqlServer, api, output) { + // quick sanity check to make sure our sqlDatabase object has "sqldatabase" as its prototype + if(!sqlServer.isPrototypeOf(sqldatabase)) { + throw "provided sqlServer does not implement sqldatabase " + sqlServer.getPrototypeOf(); + } - // before we try to populate internal database object, check to see if mysql server has any data in it - mysql.connect().query('SELECT * FROM `students` ORDER BY last ASC', function(err, rows, fields) { + // before we try to populate internal database object, check to see if SQL server has any data in it + sqlServer.query('SELECT * FROM `students` ORDER BY last ASC', function(err, rows, fields) { if(err) { - // if error, assume mysql server is not available, don't use mysql server at all. Fall back to spreadsheet implementation and advertise this to console - console.log('WARN', 'MYSQL', 'Using spreadsheet file to populate database instead. (' + err + ')'); + // if error, assume SQL server is not available, don't use SQL server at all. Fall back to spreadsheet implementation and advertise this to console + console.log('WARN', 'SQL', 'Using spreadsheet file to populate database instead. (' + err + ')'); // populate database from spreadsheet and exit return scanner.populateDatabaseFromSpreadsheet(scanner, database, function(err) { @@ -479,10 +484,10 @@ var database = { if(err) { // if fallback spreadsheet implementation errors, advertise error message and exit. return console.log( '[Fatal]: There was an error populating the database using spreadsheet' + - 'file as backup, and the mysql database as a primary means -> ' + err); + 'file as backup, and the SQL database as a primary means -> ' + err); } - database.emit('ready', ['excel']); + database.emit('ready', [ExcelExportStrategyName]); }); } @@ -490,15 +495,15 @@ var database = { // if no error fetching data, check to see if any data in database. don't take into account if table has been created or not if(rows.length) { - // if mysql table contains data, tell program - mysql.hasData = true; + // if SQL table contains data, tell program + sqlServer.hasData = true; // then, begin adding such data to internal database object scanner.populateDatabaseFromMysql(scanner, database, rows, function(err) { - if(!mysql.eventEntryCreated) { - database.initializeEventEntry(scanner, mysql, api, function() { - database.emit('ready', ['mysql']); + if(!sqlServer.eventEntryCreated) { + database.initializeEventEntry(scanner, sqlServer, api, function() { + database.emit('ready', [SqlServerExportStrategyName]); }); } @@ -506,27 +511,27 @@ var database = { } else { - // mysql database is empty. advertise that we are loading data from spreadsheet to populate mysql table - console.log('EXCEL', 'No data found on mysql server. Using spreadsheet to populate internal database.'); + // SQL database is empty. advertise that we are loading data from spreadsheet to populate SQL table + console.log('EXCEL', 'No data found on SQL server. Using spreadsheet to populate internal database.'); // if no data in database, use spreadsheet data to populate our local database object, and then - // use the newly populated local 'database' object to populate mysql server database + // use the newly populated local 'database' object to populate SQL server database scanner.populateDatabaseFromSpreadsheet(scanner, database, function(err) { if(err) { return console.log('[Fatal]: Error populating local database object from spreadsheet -> ' + err); } - // once internal database object has data in it, export data to mysql server if empty - scanner.exportDatabase(scanner, database, mysql, api, output, 'mysql', consts.EXCEL_AUTOSAVE_FILE, function(err) { + // once internal database object has data in it, export data to SQL server if empty + scanner.exportDatabase(scanner, database, sqlServer, api, output, SqlServerExportStrategyName, consts.EXCEL_AUTOSAVE_FILE, function(err) { if(err) { - console.log('An error occurred populating empty mysql database -> ' + err); - return database.emit('ready', ['excel']); + console.log('An error occurred populating empty SQL database -> ' + err); + return database.emit('ready', [ExcelExportStrategyName]); } - // begin auto-saving new data to mysql database - database.emit('ready', ['mysql']); + // begin auto-saving new data to SQL database + database.emit('ready', [SqlServerExportStrategyName]); }); }); diff --git a/js/mysql.js b/js/mysql.js index 30a20a5..6fd02d9 100755 --- a/js/mysql.js +++ b/js/mysql.js @@ -1,21 +1,17 @@ /** * define mysql connection object + * @implements sqldatabase interface **/ var consts = require('./constants.js'); var date = require('./date.js'); +var sqldatabase = require('./prototypes/sqldatabase.js'); var mysql = { - - // define mysql object properties - connection : null, // holds the connection object to the mysql server or null if not connected - eventEntryCreated : false, // flag indicating whether a mysql entry has been added (`events`) for current event - hasData : false, // flag indicating whether mysql database table contains any data - isBusy : false, // flag indicating whether a mysql query is currently ongoing - isConnected : false, // flag indicating whether a connection to mysql server has been established library : require('mysql'), // define and import node.js package /** + * @private method * creates and establishes a connection to * the mysql server * @@ -24,7 +20,7 @@ var mysql = { * @param password = {String} specifying database account password * @param database = {String} specifying the name of database to connect to **/ - connect : function(host, user, password, database) { + _connect : function(host, user, password, database) { // check to see if previous connection exists, or @params for new connection are passed if(!mysql.isConnected || (host && user && password)) { // create connection blueprint @@ -59,6 +55,16 @@ var mysql = { } }, + /** + * performs a query using the underlying database connection + * + * @param sqlQuery = {String} specifying SQL query to execute + * @param callback = {Function} to call after operation has completed successfully + **/ + query : function(sqlQuery, callback) { + mysql._connect().query(sqlQuery, callback); + }, + /** * deletes entries from table where whereLogic applies * @@ -71,8 +77,7 @@ var mysql = { deleteFrom : function(mysqlTableName, whereLogic, callback) { if(whereLogic) { // perform query only if whereLogic has been passed - mysql.connect() - .query('DELETE FROM ' + mysqlTableName + ' WHERE ' + (whereLogic || '1 = 1'), callback); + mysql.query('DELETE FROM ' + mysqlTableName + ' WHERE ' + (whereLogic || '1 = 1'), callback); } else { // fail and exit function with error callback.call(this, 'ERR: (mysqldatabasedeletionerror): no \'WHERE\' condition applies for selected logic.'); @@ -107,8 +112,7 @@ var mysql = { }); // join arrays of column names and values to add by commas and add them to our query string - mysql.connect() - .query('INSERT INTO ' + mysqlTableName + '(' + (databaseColumns.join(',')) + ') VALUES (' + valuesToAdd.join(',') + ')', + mysql.query('INSERT INTO ' + mysqlTableName + '(' + (databaseColumns.join(',')) + ') VALUES (' + valuesToAdd.join(',') + ')', // call user's callback function function(err) { // get err param if any and pass it to callback before calling @@ -128,8 +132,7 @@ var mysql = { **/ selectFrom : function(mysqlTableName, databaseColumns, whereLogic, callback) { // perform query - mysql.connect() - .query('SELECT ' + databaseColumns.join(',') + ' FROM ' + mysqlTableName + ' WHERE ' + (whereLogic || '1 = 1'), callback); + mysql.query('SELECT ' + databaseColumns.join(',') + ' FROM ' + mysqlTableName + ' WHERE ' + (whereLogic || '1 = 1'), callback); }, /** @@ -155,15 +158,16 @@ var mysql = { keyValuePairs = keyValuePairs.substring(1); // join arrays of column names and values to add by commas and add them to our query string - mysql.connect() - .query('UPDATE ' + mysqlTableName + ' SET ' + keyValuePairs + ' WHERE ' + (whereLogic || '1 = 1'), - // call user's callback function - function(err) { - // get err param if any and pass it to callback before calling and exit - return callback.call(mysql, err); - }); + mysql.query('UPDATE ' + mysqlTableName + ' SET ' + keyValuePairs + ' WHERE ' + (whereLogic || '1 = 1'), + // call user's callback function + function(err) { + // get err param if any and pass it to callback before calling and exit + return callback.call(mysql, err); + } + ); } }; +mysql.__proto__ = sqldatabase; module.exports = mysql; \ No newline at end of file diff --git a/js/output.js b/js/output.js index ad85b4d..4cbfd25 100755 --- a/js/output.js +++ b/js/output.js @@ -128,7 +128,7 @@ var output = { } // add to `attendance` table - mysql.connect().query( + mysql.query( "INSERT INTO `attendance` (`student_id`, `event_id`, `is_new`)" + "VALUES" + @@ -175,7 +175,7 @@ var output = { entry.addedToCurrentMysqlEventTable = true; // add to `attendance` table - mysql.connect().query( + mysql.query( "INSERT INTO `attendance` (`student_id`, `event_id`, `is_new`)" + "VALUES" + diff --git a/js/prototypes/baseproto.js b/js/prototypes/baseproto.js new file mode 100644 index 0000000..173a875 --- /dev/null +++ b/js/prototypes/baseproto.js @@ -0,0 +1,14 @@ +var baseproto = { + _protoName: 'baseproto', + + isPrototypeOf: function(obj) { + return obj._protoName && this._protoName === obj._protoName; + }, + + getPrototypeOf: function() { + return this._protoName; + } +} + + +module.exports = baseproto; \ No newline at end of file diff --git a/js/prototypes/sqldatabase.js b/js/prototypes/sqldatabase.js new file mode 100644 index 0000000..0bcc4d6 --- /dev/null +++ b/js/prototypes/sqldatabase.js @@ -0,0 +1,89 @@ +/** + * define sqldatabase prototype +**/ + +var baseproto = require('./baseproto.js'); + +var sqldatabase = { + _protoName : 'sqldatabase', + + // define sqldatabase object properties + connection : null, // holds the connection object to the SQL server or null if not connected + eventEntryCreated : false, // flag indicating whether a SQL entry has been added (`events`) for current event + hasData : false, // flag indicating whether SQL database table contains any data + isBusy : false, // flag indicating whether a SQL query is currently ongoing + isConnected : false, // flag indicating whether a connection to SQL server has been established + + /** + * performs a query using the underlying database connection + * + * @param sqlQuery = {String} specifying SQL query to execute + * @param callback = {Function} to call after operation has completed successfully + **/ + query : function(sqlQuery, callback) { + throw "unimplemented query method"; + }, + + /** + * deletes entries from table where whereLogic applies + * + * @param sqlTableName = {Object} entry object from local 'database' object + * @param whereLogic = {String} containing equality to use to target the selection of a specific row + * @param callback = {Function} to call after operation has completed successfully + * + * for data protection, if @param whereLogic is 'null', nothing should be deleted / returned + **/ + deleteFrom : function(sqlTableName, whereLogic, callback) { + throw "unimplemented deleteFrom method"; + }, + + /** + * safely closes the SQL server connection + **/ + end : function() { + throw "unimplemented end method"; + }, + + /** + * inserts new entry to SQL database + * + * @param sqlTableName = {Object} entry object from local 'database' object + * @param databaseColumns = {Array} containing names of SQL table columns to insert values into + * @param valuesToAdd = {Array} containing entry values to add + * @param callback = {Function} to call after operation has completed successfully + **/ + insertInto : function(sqlTableName, databaseColumns, valuesToAdd, callback) { + throw "unimplemented insertInto method"; + }, + + /** + * selects entries from table, using passed logic + * + * @param sqlTableName = {Object} entry object from local 'database' object + * @param databaseColumns = {Array} containing names of SQL table columns to select + * @param whereLogic = {String} containing equality to use to target the selection of a specific row + * @param callback = {Function} to call after operation has completed successfully + * + * if @param whereLogic is 'null', all rows are selected and returned + **/ + selectFrom : function(sqlTableName, databaseColumns, whereLogic, callback) { + throw "unimplemented selectFrom method"; + }, + + /** + * updates entry in database table, using passed logic + * + * @param sqlTableName = {Object} entry object from local 'database' object + * @param databaseColumns = {Array} containing names of SQL table columns to update values + * @param updatedValues = {Array} containing updated entry values + * @param whereLogic = {String} containing equality to use to target the update of a specific row + * @param callback = {Function} to call after operation has completed successfully + **/ + update : function(sqlTableName, databaseColumns, updatedValues, whereLogic, callback) { + throw "unimplemented update method"; + } + +}; + +sqldatabase.__proto__ = baseproto; +module.exports = sqldatabase; \ No newline at end of file diff --git a/js/scanner.js b/js/scanner.js index 927db7c..229ef46 100755 --- a/js/scanner.js +++ b/js/scanner.js @@ -86,7 +86,7 @@ var _scanner = { syncAttendanceTableWithAPIServer: function(mysql, api, callback) { // get dataset containing hash of latest attendance result - mysql.connect().query('SELECT MD5(concat(student_id, event_id, is_new, COUNT(*))) AS md5, COUNT(*) AS total FROM `attendance` ORDER BY student_id DESC', function(err, rows) { + mysql.query('SELECT MD5(concat(student_id, event_id, is_new, COUNT(*))) AS md5, COUNT(*) AS total FROM `attendance` ORDER BY student_id DESC', function(err, rows) { if(err) { return console.log('API', 'SYNC', 'ERR', err); diff --git a/js/tests/utilities.js b/js/tests/utilities.js index 0a06f00..1d07efa 100755 --- a/js/tests/utilities.js +++ b/js/tests/utilities.js @@ -30,7 +30,7 @@ if(!USE_CLOUD_DB) { } -mysql.connect(function(err) { +mysql._connect(function(err) { if(err) { return console.log('MYSQL', 'ERR', err);