Initial upload with MSSQL and MariaDB implemented.

This commit is contained in:
2025-08-21 02:46:01 -04:00
commit 57430daf33
10 changed files with 3043 additions and 0 deletions

View File

@@ -0,0 +1,81 @@
// /services/dbconnector/connectionManager.js
require('dotenv').config();
const sql = require('mssql');
const oracledb = require('oracledb');
const mysql = require('mysql2/promise');
const { Client } = require('pg');
/**
* Gets a connection based on the configured database type.
*
* @returns {Promise<Object>} A promise that resolves with the database connection.
*/
async function getConnection() {
const dbType = process.env.DB_TYPE;
const config = getDBConfig(dbType);
switch (dbType) {
case 'MSSQL':
return await sql.connect(config);
case 'ORACLE':
return await oracledb.getConnection(config);
case 'MARIADB':
return await mysql.createConnection(config);
case 'POSTGRES':
const client = new Client(config);
await client.connect();
return client;
default:
throw new Error('Unsupported database type');
}
}
/**
* Fetches the configuration for a specified database type from environment variables.
*
* @param {string} dbType - Type of database (e.g., 'MSSQL', 'ORACLE').
* @returns {Object} The database configuration object.
*/
function getDBConfig(dbType) {
switch (dbType) {
case 'MSSQL':
return {
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
server: process.env.DB_SERVER,
database: process.env.DB_DATABASE,
options: {
encrypt: true, // for secure connection
trustServerCertificate: process.env.DB_TRUST_CERT
}
};
case 'ORACLE':
return {
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
connectString: process.env.ORACLE_CONNECTION_STRING
};
case 'MARIADB':
return {
host: process.env.DB_SERVER,
port: parseInt(process.env.DB_PORT, 10) || 3006,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE,
namedPlaceholders: true
};
case 'POSTGRES':
return {
host: process.env.DB_SERVER,
port: parseInt(process.env.DB_PORT, 10) || 5432,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
};
default:
throw new Error('Unsupported database type');
}
}
module.exports = { getConnection };

View File

@@ -0,0 +1,172 @@
// /services/dbconnector/paramLibrary.js
const sql = require('mssql');
const oracledb = require('oracledb');
/**
* Formats query parameters based on their keys and types, specific to each database type.
*
* @param {Object} params - An object containing the parameters to format.
* @returns {Promise<Object>} A promise that resolves with formatted parameters.
*/
async function formatQueryParams(params) {
const { DB_TYPE } = process.env;
let formattedParams = {};
console.log(`Formatting query parameters for ${DB_TYPE.toUpperCase()}...`); // Debugging log
console.log('Raw params:', params); // Log the raw parameters received
switch (DB_TYPE.toUpperCase()) {
case 'MSSQL':
formattedParams = formatMSSQLParams(params);
break;
case 'ORACLE':
formattedParams = formatOracleParams(params);
break;
case 'MARIADB':
formattedParams = formatMariaDBParams(params);
break;
case 'POSTGRES':
formattedParams = formatPostgresParams(params);
break;
default:
throw new Error('Unsupported database type');
}
console.log('Formatted params:', formattedParams); // Log the formatted parameters
return formattedParams;
}
/**
* Formats query parameters for MSSQL.
*
* @param {Object} params - An object containing the parameters to format.
* @returns {Object} Formatted parameters.
*/
function formatMSSQLParams(params) {
const formattedParams = {};
for (const key in params) {
if (key === 'ID') {
console.log(`Formatting ID: ${params[key]}`); // Debugging log
formattedParams[key] = { type: sql.UniqueIdentifier, value: params[key] };
} else if (
['Name', 'Description', 'Building', 'Owner', 'Role', 'Location', 'Route', 'Email', 'DisplayName', 'Team'].includes(key)
) {
formattedParams[key] = { type: sql.NVarChar(255), value: params[key] };
} else if (key === 'Username' || key === 'AuthenticatedUsername') {
formattedParams[key] = { type: sql.NVarChar(50), value: params[key].toLowerCase() }; // Normalize to lowercase
} else if (key === 'UITheme' || key === 'UITHeme') {
formattedParams[key] = { type: sql.NVarChar(50), value: params[key] };
} else if (['Bio', 'PasswordHash', 'Image', 'AvatarURL'].includes(key)) {
formattedParams[key] = { type: sql.Text, value: params[key] };
} else if (key === 'SQL_USER') {
formattedParams[key] = { type: sql.Bit, value: params[key] }; // Support for bit data type
} else if (typeof params[key] === 'string') {
formattedParams[key] = { type: sql.NVarChar(255), value: params[key] };
} else if (typeof params[key] === 'number') {
formattedParams[key] = { type: sql.Int, value: params[key] };
}
}
return formattedParams;
}
// ... rest of the file ...
/**
* Formats query parameters for Oracle.
*
* @param {Object} params - An object containing the parameters to format.
* @returns {Object} Formatted parameters.
*/
function formatOracleParams(params) {
const formattedParams = {};
for (const key in params) {
if (key === 'ID') {
formattedParams[key] = { type: oracledb.STRING, value: params[key] }; // UUID can be handled as a string
} else if (
['Name', 'Description', 'Building', 'Owner', 'Role', 'Location', 'Route', 'Email', 'DisplayName', 'Team'].includes(key)
) {
formattedParams[key] = { type: oracledb.STRING, value: params[key] };
} else if (key === 'Username' || key === 'AuthenticatedUsername') {
formattedParams[key] = { type: oracledb.STRING, value: params[key].toLowerCase() }; // Normalize to lowercase
} else if (key === 'UITheme' || key === 'UITHeme') {
formattedParams[key] = { type: oracledb.STRING, value: params[key] };
} else if (['Bio', 'PasswordHash', 'Image', 'AvatarURL'].includes(key)) {
formattedParams[key] = { type: oracledb.CLOB, value: params[key] };
} else if (key === 'SQL_USER') {
formattedParams[key] = { type: oracledb.BOOLEAN, value: params[key] }; // Support for bit data type
} else if (typeof params[key] === 'string') {
formattedParams[key] = { type: oracledb.STRING, value: params[key] };
} else if (typeof params[key] === 'number') {
formattedParams[key] = { type: oracledb.NUMBER, value: params[key] };
}
}
return formattedParams;
}
/**
* Formats query parameters for MariaDB.
*
* @param {Object} params - An object containing the parameters to format.
* @returns {Object} Formatted parameters.
*/
function formatMariaDBParams(params) {
const formattedParams = {};
for (const key in params) {
if (key === 'ID') {
formattedParams[key] = { type: 'char', value: params[key] }; // UUID can be handled as a string
} else if (
['Name', 'Description', 'Building', 'Owner', 'Role', 'Location', 'Route', 'Email', 'DisplayName', 'Team'].includes(key)
) {
formattedParams[key] = { type: 'string', value: params[key] };
} else if (key === 'Username' || key === 'AuthenticatedUsername') {
formattedParams[key] = { type: 'string', value: params[key].toLowerCase() }; // Normalize to lowercase
} else if (key === 'UITheme' || key === 'UITHeme') {
formattedParams[key] = { type: 'string', value: params[key] };
} else if (['Bio', 'PasswordHash', 'Image', 'AvatarURL'].includes(key)) {
formattedParams[key] = { type: 'text', value: params[key] };
} else if (key === 'SQL_USER') {
formattedParams[key] = { type: 'boolean', value: params[key] }; // Support for bit data type
} else if (typeof params[key] === 'string') {
formattedParams[key] = { type: 'string', value: params[key] };
} else if (typeof params[key] === 'number') {
formattedParams[key] = { type: 'int', value: params[key] };
}
}
return formattedParams;
}
/**
* Formats query parameters for PostgreSQL.
*
* @param {Object} params - An object containing the parameters to format.
* @returns {Object} Formatted parameters.
*/
function formatPostgresParams(params) {
const formattedParams = {};
for (const key in params) {
if (key === 'ID') {
formattedParams[key] = { type: 'uuid', value: params[key] };
} else if (
['Name', 'Description', 'Building', 'Owner', 'Role', 'Location', 'Route', 'Email', 'DisplayName', 'Team'].includes(key)
) {
formattedParams[key] = { type: 'varchar', value: params[key] };
} else if (key === 'Username' || key === 'AuthenticatedUsername') {
formattedParams[key] = { type: 'varchar', value: params[key].toLowerCase() }; // Normalize to lowercase
} else if (key === 'UITheme' || key === 'UITHeme') {
formattedParams[key] = { type: 'varchar', value: params[key] };
} else if (['Bio', 'PasswordHash', 'Image', 'AvatarURL'].includes(key)) {
formattedParams[key] = { type: 'text', value: params[key] };
} else if (key === 'SQL_USER') {
formattedParams[key] = { type: 'boolean', value: params[key] }; // Support for bit data type
} else if (typeof params[key] === 'string') {
formattedParams[key] = { type: 'varchar', value: params[key] };
} else if (typeof params[key] === 'number') {
formattedParams[key] = { type: 'int', value: params[key] };
}
}
return formattedParams;
}
module.exports = { formatQueryParams };

View File

@@ -0,0 +1,294 @@
// /services/dbconnector/queryConstructor.js
/**
* Generates a SQL query string based on table, operation, and parameters.
*
* @param {string} table - The name of the database table.
* @param {string} operation - CRUD operation (e.g., 'CREATE', 'READ').
* @param {Object} params - Parameters for the SQL query.
* @returns {Promise<string>} A promise that resolves with the constructed SQL query string.
*/
async function generateQuery(table, operation, params) {
const { DB_TYPE } = process.env;
let query = '';
switch (operation.toUpperCase()) {
case 'CREATE':
validateCreateParams(params);
query = generateCreateQuery(DB_TYPE, table, params);
break;
case 'READ':
validateReadParams(params);
query = generateReadQuery(DB_TYPE, table, params);
break;
case 'UPDATE':
validateUpdateParams(params);
query = generateUpdateQuery(DB_TYPE, table, params);
break;
case 'DELETE':
validateDeleteParams(params);
query = generateDeleteQuery(DB_TYPE, table, params);
break;
/* case 'COMPARE':
validateCompareParams(params);
query = generateCompareQuery(DB_TYPE, table, params);
break; */
default:
throw new Error('Unsupported operation');
}
return query;
}
/*
/**
* Validates parameters for CREATE operation.
*
* @param {Object} params - Parameters for the SQL query.
*/
function validateCreateParams(params) {
if (!params || typeof params !== 'object' || Object.keys(params).length === 0) {
throw new Error('Missing or invalid parameters for CREATE operation. Please provide a non-empty object with necessary fields.');
}
}
/**
* Validates parameters for READ operation.
*
* @param {Object} params - Parameters for the SQL query.
*/
function validateReadParams(params) {
if (params && typeof params !== 'object') {
throw new Error('Missing or invalid parameters for READ operation. Please provide an object with "ID" field.');
}
}
/**
* Validates parameters for UPDATE operation.
*
* @param {Object} params - Parameters for the SQL query.
*/
function validateUpdateParams(params) {
if (!params || typeof params !== 'object' || !params.ID || Object.keys(params).length <= 1) {
throw new Error('Missing or invalid parameters for UPDATE operation. Please provide an object with "ID" field and at least one other field to update.');
}
}
/**
* Validates parameters for DELETE operation.
*
* @param {Object} params - Parameters for the SQL query.
*/
function validateDeleteParams(params) {
if (!params || typeof params !== 'object' || !params.ID) {
throw new Error('Missing or invalid parameters for DELETE operation. Please provide an object with "ID" field.');
}
}
/**
* Validates parameters for COMPARE operation.
*
* @param {Object} params - Parameters for the SQL query.
*
function validateCompareParams(params) {
if (!params || typeof params !== 'object' || !params.compareTable || !params.idField) {
throw new Error('Missing or invalid parameters for COMPARE operation. Please provide an object with "compareTable" and "idField" fields.');
}
}*/
/**
* Generates a CREATE SQL query string based on database type, table, and parameters.
*
* @param {string} dbType - Type of database (e.g., 'MSSQL', 'ORACLE').
* @param {string} table - The name of the database table.
* @param {Object} params - Parameters for the SQL query.
* @returns {string} The constructed CREATE SQL query string.
*/
function generateCreateQuery(dbType, table, params) {
const columns = Object.keys(params).join(', ');
// Generate placeholders based on dbType
let placeholders;
switch (dbType.toUpperCase()) {
case 'MSSQL':
placeholders = Object.keys(params)
.map(key => `@${key}`)
.join(', ');
break;
case 'POSTGRES':
placeholders = Object.keys(params).map(() => '?').join(', '); // Use ? for PostgreSQL
break;
case 'MARIADB':
placeholders = Object.keys(params).map(() => `?`).join(', '); // Use ? for MariaDB
break;
case 'ORACLE':
throw new Error('CREATE operation is not supported for ORACLE in this implementation.');
default:
throw new Error('Unsupported database type for this operation!');
}
return `INSERT INTO ${table} (${columns}) VALUES (${placeholders});`;
}
/**
* Generates a READ SQL query string based on database type, table, and parameters.
*
* @param {string} dbType - Type of database (e.g., 'MSSQL', 'ORACLE').
* @param {string} table - The name of the database table.
* @param {Object} params - Parameters for the SQL query.
* @returns {string} The constructed READ SQL query string.
*/
function generateReadQuery(dbType, table, params) {
// Start with the basic SELECT statement
let query = `SELECT * FROM ${table}`;
// Prepare an array to hold WHERE conditions
const whereConditions = [];
// Iterate over each parameter in params
for (const key in params) {
if (params.hasOwnProperty(key)) {
const value = params[key];
// Construct the condition based on dbType
let condition;
switch (dbType.toUpperCase()) {
case 'MSSQL':
condition = `${key} = @${key}`;
break;
case 'POSTGRES':
condition = `${key} = $1`;
break;
case 'MARIADB':
condition = `${key} = ?`;
break;
default:
throw new Error('Unsupported database type');
}
whereConditions.push(condition);
}
}
// If there are any WHERE conditions, append them to the query
if (whereConditions.length > 0) {
query += ` WHERE ${whereConditions.join(' AND ')}`;
}
return query;
}
/**
* Generates a UPDATE SQL query string based on database type, table, and parameters.
*
* @param {string} dbType - Type of database (e.g., 'MSSQL', 'ORACLE').
* @param {string} table - The name of the database table.
* @param {Object} params - Parameters for the SQL query.
* @returns {Promise<string>} The constructed UPDATE SQL query string.
*/
async function generateUpdateQuery(dbType, table, params) {
const updateClause = Object.keys(params)
.filter(key => key !== 'ID') // Exclude ID from update clause
.map(key => {
switch (dbType.toUpperCase()) {
case 'MSSQL':
return `${key} = @${key}`;
case 'POSTGRES':
return `${key} = $1`;
case 'MARIADB':
return `${key} = ?`;
default:
throw new Error('Unsupported database type');
}
})
.join(', ');
let whereClause;
switch (dbType.toUpperCase()) {
case 'MSSQL':
whereClause = `WHERE ID = @ID`;
break;
case 'POSTGRES':
whereClause = `WHERE ID = $2`;
break;
case 'MARIADB':
whereClause = `WHERE ID = ?`;
break;
default:
throw new Error('Unsupported database type');
}
return `UPDATE ${table} SET ${updateClause} ${whereClause};`;
}
/**
* Generates a DELETE SQL query string based on database type, table, and parameters.
*
* @param {string} dbType - Type of database (e.g., 'MSSQL', 'ORACLE').
* @param {string} table - The name of the database table.
* @param {Object} params - Parameters for the SQL query.
* @returns {Promise<string>} The constructed DELETE SQL query string.
*/
async function generateDeleteQuery(dbType, table, params) {
let idParam = params.ID || 'ID';
switch (dbType.toUpperCase()) {
case 'MSSQL':
return `DELETE FROM ${table} WHERE ID = @ID;`;
case 'POSTGRES':
return `DELETE FROM ${table} WHERE ID = $1;`; // Use $1 for PostgreSQL
case 'MARIADB':
return `DELETE FROM ${table} WHERE ID = ?;`;
case 'ORACLE':
throw new Error('DELETE operation is not supported for ORACLE in this implementation.');
default:
throw new Error('Unsupported database type!');
}
}
/*
*
* I intend to implement "compare" functionality but haven't yet decided how to do so. This is some spitballed code that I came up with for this exercise, and I hope to complete work on it in the future.
/**
* Generates a COMPARE SQL query string based on database type, table, and parameters.
*
* @param {string} dbType - Type of database (e.g., 'MSSQL', 'ORACLE').
* @param {Object} params - Parameters for the SQL query.
* @returns {string} The constructed COMPARE SQL query string.
*
function generateCompareQuery(dbType, table, params) {
const { compareTable, idField } = params;
switch (dbType.toUpperCase()) {
case 'MSSQL':
return `
SELECT a.*
FROM ${table} a
JOIN ${compareTable} b ON a.${idField} = b.${idField}
`;
case 'ORACLE':
return `
SELECT a.*
FROM ${table} a
JOIN ${compareTable} b ON a.${idField} = b.${idField}
`;
case 'POSTGRES':
return `
SELECT a.*
FROM ${table} a
JOIN ${compareTable} b ON a.${idField} = b.${idField}
`;
case 'MARIADB':
return `
SELECT a.*
FROM ${table} a
JOIN ${compareTable} b ON a.${idField} = b.${idField}
`;
default:
throw new Error('Unsupported database type');
}
}*/
module.exports = { generateQuery };

View File

@@ -0,0 +1,146 @@
// /services/dbconnector/queryExecutor.js
require('dotenv').config();
const connectionManager = require('./connectionManager');
const queryConstructor = require('./queryConstructor');
const paramLibrary = require('./paramLibrary');
/**
* Executes a query against the configured database type.
*
* @param {string} table - The name of the database table.
* @param {string} operation - CRUD operation (e.g., 'CREATE', 'READ').
* @param {Object} config - Database configuration object.
* @param {Object} params - Parameters for the SQL query.
* @returns {Promise<Object>} A promise that resolves with the result of the query execution.
*/
async function executeQuery(table, operation, params) {
console.log(`Executing query for ${operation.toUpperCase()} on table ${table}...`); // Debugging log
console.log('Raw parameters:', params); // Log the raw parameters received
const query = await queryConstructor.generateQuery(table, operation, params);
let result;
try {
const formattedParams = await paramLibrary.formatQueryParams(params);
const connection = await connectionManager.getConnection();
switch (process.env.DB_TYPE.toUpperCase()) {
case 'MSSQL':
console.log('Executing MSSQL query:', query); // Debugging log
console.log('Formatted parameters for MSSQL:', formattedParams); // Log the formatted parameters
result = await executeMSSQLQuery(connection, query, formattedParams);
break;
case 'ORACLE':
console.log('Executing Oracle query:', query); // Debugging log
console.log('Formatted parameters for Oracle:', formattedParams); // Log the formatted parameters
result = await executeOracleQuery(connection, query, formattedParams);
break;
case 'MARIADB':
console.log('Executing MariaDB query:', query); // Debugging log
console.log('Formatted parameters for MariaDB:', formattedParams); // Log the formatted parameters
result = await executeMariaDBQuery(connection, query, formattedParams);
break;
case 'POSTGRES':
console.log('Executing PostgreSQL query:', query); // Debugging log
console.log('Formatted parameters for PostgreSQL:', formattedParams); // Log the formatted parameters
result = await executePostgresQuery(connection, query, formattedParams);
break;
default:
throw new Error('Unsupported database type');
}
} catch (err) {
console.error('Error executing query:', err.message);
throw err;
}
switch (operation.toUpperCase()) {
case 'READ':
return result.recordset || [];
case 'UPDATE':
case 'DELETE':
return { success: true, affectedRows: result.affectedRows };
case 'CREATE':
return { success: true };
}
return result;
}
async function executeMSSQLQuery(connection, query, params) {
console.log('Binding parameters for MSSQL:', params); // Debugging log
const request = connection.request();
// Bind each parameter with its name and value
for (const key in params) {
if (params[key]) {
request.input(key, params[key].type, params[key].value);
}
}
try {
console.log('Executing MSSQL query:', query); // Debugging log
const result = await request.query(query);
return { recordset: result.recordset, affectedRows: result.rowsAffected };
} catch (error) {
console.error('Error executing MSSQL query:', error.message);
throw error;
}
}
async function executeOracleQuery(connection, query, params) {
const binds = {};
// Bind each parameter with its name and value
Object.keys(params).forEach((key, index) => {
binds[`param${index + 1}`] = { type: params[key].type, val: params[key].value };
});
const options = {
outFormat: require('oracledb').OUT_FORMAT_OBJECT
};
try {
console.log('Executing Oracle query:', query); // Debugging log
console.log('Binding parameters for Oracle:', binds); // Debugging log
const result = await connection.execute(query, binds, options);
return { recordset: result.rows, affectedRows: result.rowsAffected };
} catch (error) {
console.error('Error executing Oracle query:', error.message);
throw error;
}
}
async function executeMariaDBQuery(connection, query, params) {
try {
// Extract parameter values in order
const paramValues = Object.keys(params).map(key => params[key].value);
console.log('Executing MariaDB query:', query); // Debugging log
console.log('Binding parameters for MariaDB:', paramValues); // Debugging log
const [result] = await connection.execute(query, paramValues);
return { recordset: result };
} finally {
await connection.end();
}
}
async function executePostgresQuery(connection, query, params) {
try {
// Extract parameter values in order
const paramValues = Object.keys(params).map(key => params[key].value);
console.log('Executing PostgreSQL query:', query); // Debugging log
console.log('Binding parameters for PostgreSQL:', paramValues); // Debugging log
const result = await connection.query(query, paramValues);
return { recordset: result.rows };
} catch (error) {
console.error('Error executing PostgreSQL query:', error.message);
throw error;
}
}
module.exports = { executeQuery };