Added parsing for partialMatch parameter in generateReadQuery function. To see all contents, send no params. To see exact matches, send no partialMatch param or send partialMatch: false. Send partialMatch: true to see all partial matches.

Additionally, moved parameter definitions out of paramLibrary into paramsConfig.json - you can now add/remove/change parameter data type definitions inside of this file. If you get a console error of "Unsupported Param Type" then you may need to also update paramLibrary with handling for the data type you're using- use the existing definitions in functions such as formatMSSQLParams as a guide.

Added testing for the partialMatch parameter to client.js.
This commit is contained in:
2025-08-21 22:10:50 -04:00
parent 136ce71a0e
commit 4e8998d5ed
5 changed files with 279 additions and 98 deletions

View File

@@ -61,7 +61,16 @@ async function testQuery(table, operation, params) {
} ); } );
// READ: See all rows in a table // READ: See all rows in a table
await testQuery('Test', 'READ'); await testQuery('Test', 'READ', {
Name: 'Tested item name',
});
// READ: See all rows in a table
await testQuery('Test', 'READ', {
partialMatch: true,
Name: 'Test',
});
// DELETE: Ensure the ID is present // DELETE: Ensure the ID is present
await testQuery('Test', 'DELETE', { await testQuery('Test', 'DELETE', {

View File

@@ -47,7 +47,7 @@ function getDBConfig(dbType) {
database: process.env.DB_DATABASE, database: process.env.DB_DATABASE,
options: { options: {
encrypt: true, // for secure connection encrypt: true, // for secure connection
trustServerCertificate: process.env.DB_TRUST_CERT trustServerCertificate: true
} }
}; };
case 'ORACLE': case 'ORACLE':

View File

@@ -3,6 +3,14 @@
const sql = require('mssql'); const sql = require('mssql');
const oracledb = require('oracledb'); const oracledb = require('oracledb');
// Load paramsConfig.json once at the start
let paramsConfig;
try {
paramsConfig = require('./paramsConfig.json');
} catch (error) {
console.error('Error reading JSON config:', error);
}
/** /**
* Formats query parameters based on their keys and types, specific to each database type. * Formats query parameters based on their keys and types, specific to each database type.
* *
@@ -18,16 +26,16 @@ async function formatQueryParams(params) {
switch (DB_TYPE.toUpperCase()) { switch (DB_TYPE.toUpperCase()) {
case 'MSSQL': case 'MSSQL':
formattedParams = formatMSSQLParams(params); formattedParams = formatMSSQLParams(params, paramsConfig.mssql);
break; break;
case 'ORACLE': case 'ORACLE':
formattedParams = formatOracleParams(params); formattedParams = formatOracleParams(params, paramsConfig.oracle);
break; break;
case 'MARIADB': case 'MARIADB':
formattedParams = formatMariaDBParams(params); formattedParams = formatMariaDBParams(params, paramsConfig.mariaDB);
break; break;
case 'POSTGRES': case 'POSTGRES':
formattedParams = formatPostgresParams(params); formattedParams = formatPostgresParams(params, paramsConfig.postgres);
break; break;
default: default:
throw new Error('Unsupported database type'); throw new Error('Unsupported database type');
@@ -41,66 +49,90 @@ async function formatQueryParams(params) {
* Formats query parameters for MSSQL. * Formats query parameters for MSSQL.
* *
* @param {Object} params - An object containing the parameters to format. * @param {Object} params - An object containing the parameters to format.
* @param {Object} config - Database-specific configuration from JSON.
* @returns {Object} Formatted parameters. * @returns {Object} Formatted parameters.
*/ */
function formatMSSQLParams(params) { function formatMSSQLParams(params, config) {
const formattedParams = {}; const formattedParams = {};
for (const key in params) { for (const key in params) {
if (key === 'ID') { if (!config[key]) {
console.log(`Formatting ID: ${params[key]}`); // Debugging log console.warn(`Parameter '${key}' not defined in the configuration.`);
formattedParams[key] = { type: sql.UniqueIdentifier, value: params[key] }; continue;
} 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] };
} }
const paramConfig = config[key];
let type;
switch (paramConfig.type.toLowerCase()) {
case 'uniqueidentifier':
type = sql.UniqueIdentifier;
break;
case 'nvarchar':
if (!paramConfig.maxLength) {
throw new Error(`Parameter '${key}' is missing maxLength in configuration.`);
}
type = sql.NVarChar(paramConfig.maxLength);
break;
case 'text':
type = sql.Text;
break;
case 'int':
type = sql.Int;
break;
case 'bit':
type = sql.Bit;
break;
default:
throw new Error(`Unsupported parameter type: ${paramConfig.type} for parameter '${key}'`);
}
formattedParams[key] = { type, value: params[key] };
} }
return formattedParams; return formattedParams;
} }
// ... rest of the file ...
/** /**
* Formats query parameters for Oracle. * Formats query parameters for Oracle.
* *
* @param {Object} params - An object containing the parameters to format. * @param {Object} params - An object containing the parameters to format.
* @param {Object} config - Database-specific configuration from JSON.
* @returns {Object} Formatted parameters. * @returns {Object} Formatted parameters.
*/ */
function formatOracleParams(params) { function formatOracleParams(params, config) {
const formattedParams = {}; const formattedParams = {};
for (const key in params) { for (const key in params) {
if (key === 'ID') { if (!config[key]) {
formattedParams[key] = { type: oracledb.STRING, value: params[key] }; // UUID can be handled as a string console.warn(`Parameter '${key}' not defined in the configuration.`);
} else if ( continue;
['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] };
} }
const paramConfig = config[key];
let type;
switch (paramConfig.type.toLowerCase()) {
case 'string':
type = oracledb.STRING;
break;
case 'varchar2':
if (!paramConfig.maxLength) {
throw new Error(`Parameter '${key}' is missing maxLength in configuration.`);
}
type = oracledb.VARCHAR2(paramConfig.maxLength);
break;
case 'clob':
type = oracledb.CLOB;
break;
case 'number':
type = oracledb.NUMBER;
break;
case 'boolean':
type = oracledb.BOOLEAN;
break;
default:
throw new Error(`Unsupported parameter type: ${paramConfig.type} for parameter '${key}'`);
}
formattedParams[key] = { type, value: params[key] };
} }
return formattedParams; return formattedParams;
} }
@@ -109,30 +141,47 @@ function formatOracleParams(params) {
* Formats query parameters for MariaDB. * Formats query parameters for MariaDB.
* *
* @param {Object} params - An object containing the parameters to format. * @param {Object} params - An object containing the parameters to format.
* @param {Object} config - Database-specific configuration from JSON.
* @returns {Object} Formatted parameters. * @returns {Object} Formatted parameters.
*/ */
function formatMariaDBParams(params) { function formatMariaDBParams(params, config) {
const formattedParams = {}; const formattedParams = {};
for (const key in params) { for (const key in params) {
if (key === 'ID') { if (!config[key]) {
formattedParams[key] = { type: 'char', value: params[key] }; // UUID can be handled as a string console.warn(`Parameter '${key}' not defined in the configuration.`);
} else if ( continue;
['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] };
} }
const paramConfig = config[key];
let type;
switch (paramConfig.type.toLowerCase()) {
case 'char':
if (!paramConfig.length) {
throw new Error(`Parameter '${key}' is missing length in configuration.`);
}
type = { type: 'CHAR', length: paramConfig.length };
break;
case 'uuid':
type = 'uuid';
break;
case 'string':
type = 'string';
break;
case 'text':
type = 'text';
break;
case 'int':
type = 'int';
break;
case 'boolean':
type = 'boolean';
break;
default:
throw new Error(`Unsupported parameter type: ${paramConfig.type} for parameter '${key}'`);
}
formattedParams[key] = { type, value: params[key] };
} }
return formattedParams; return formattedParams;
} }
@@ -141,32 +190,47 @@ function formatMariaDBParams(params) {
* Formats query parameters for PostgreSQL. * Formats query parameters for PostgreSQL.
* *
* @param {Object} params - An object containing the parameters to format. * @param {Object} params - An object containing the parameters to format.
* @param {Object} config - Database-specific configuration from JSON.
* @returns {Object} Formatted parameters. * @returns {Object} Formatted parameters.
*/ */
function formatPostgresParams(params) { function formatPostgresParams(params, config) {
const formattedParams = {}; const formattedParams = {};
for (const key in params) { for (const key in params) {
if (key === 'ID') { if (!config[key]) {
formattedParams[key] = { type: 'uuid', value: params[key] }; console.warn(`Parameter '${key}' not defined in the configuration.`);
} else if ( continue;
['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] };
} }
const paramConfig = config[key];
let type;
switch (paramConfig.type.toLowerCase()) {
case 'uuid':
type = 'uuid';
break;
case 'varchar':
if (!paramConfig.length) {
throw new Error(`Parameter '${key}' is missing length in configuration.`);
}
type = { type: 'VARCHAR', length: paramConfig.length };
break;
case 'text':
type = 'text';
break;
case 'int':
type = 'int';
break;
case 'boolean':
type = 'boolean';
break;
default:
throw new Error(`Unsupported parameter type: ${paramConfig.type} for parameter '${key}'`);
}
formattedParams[key] = { type, value: params[key] };
} }
return formattedParams; return formattedParams;
} }
module.exports = { formatQueryParams }; module.exports = { formatQueryParams };

View File

@@ -0,0 +1,90 @@
{
"mssql": {
"ID": { "type": "uniqueidentifier" },
"Name": { "type": "nvarchar", "maxLength": 255 },
"Description": { "type": "nvarchar", "maxLength": 255 },
"Building": { "type": "nvarchar", "maxLength": 255 },
"Owner": { "type": "nvarchar", "maxLength": 255 },
"Role": { "type": "nvarchar", "maxLength": 255 },
"Location": { "type": "nvarchar", "maxLength": 255 },
"Route": { "type": "nvarchar", "maxLength": 255 },
"Email": { "type": "nvarchar", "maxLength": 255 },
"DisplayName": { "type": "nvarchar", "maxLength": 255 },
"Team": { "type": "nvarchar", "maxLength": 255 },
"Username": { "type": "nvarchar", "maxLength": 50 },
"AuthenticatedUsername": { "type": "nvarchar", "maxLength": 50 },
"UITheme": { "type": "nvarchar", "maxLength": 50 },
"Bio": { "type": "text" },
"PasswordHash": { "type": "text" },
"Image": { "type": "text" },
"AvatarURL": { "type": "text" },
"SQL_USER": { "type": "bit" },
"Text": { "type": "nvarchar", "maxLength": 255 }
},
"oracle": {
"ID": { "type": "string" },
"Name": { "type": "varchar2", "maxLength": 255 },
"Description": { "type": "varchar2", "maxLength": 255 },
"Text": { "type": "varchar2", "maxLength": 255 },
"Building": { "type": "varchar2", "maxLength": 255 },
"Owner": { "type": "varchar2", "maxLength": 255 },
"Role": { "type": "varchar2", "maxLength": 255 },
"Location": { "type": "varchar2", "maxLength": 255 },
"Route": { "type": "varchar2", "maxLength": 255 },
"Email": { "type": "varchar2", "maxLength": 255 },
"DisplayName": { "type": "varchar2", "maxLength": 255 },
"Team": { "type": "varchar2", "maxLength": 255 },
"Username": { "type": "varchar2", "maxLength": 50 },
"AuthenticatedUsername": { "type": "varchar2", "maxLength": 50 },
"UITheme": { "type": "varchar2", "maxLength": 50 },
"Bio": { "type": "clob" },
"PasswordHash": { "type": "clob" },
"Image": { "type": "clob" },
"AvatarURL": { "type": "clob" },
"SQL_USER": { "type": "boolean" }
},
"mariaDB": {
"ID": { "type": "uuid" },
"Name": { "type": "string" },
"Description": { "type": "string" },
"Text": { "type": "string" },
"Building": { "type": "string" },
"Owner": { "type": "string" },
"Role": { "type": "string" },
"Location": { "type": "string" },
"Route": { "type": "string" },
"Email": { "type": "string" },
"DisplayName": { "type": "string" },
"Team": { "type": "string" },
"Username": { "type": "string" },
"AuthenticatedUsername": { "type": "string" },
"UITheme": { "type": "string" },
"Bio": { "type": "text" },
"PasswordHash": { "type": "text" },
"Image": { "type": "text" },
"AvatarURL": { "type": "text" },
"SQL_USER": { "type": "boolean" }
},
"postgres": {
"ID": { "type": "uuid" },
"Name": { "type": "varchar", "length": 255 },
"Description": { "type": "varchar", "length": 255 },
"Text": { "type": "varchar", "length": 255 },
"Building": { "type": "varchar", "length": 255 },
"Owner": { "type": "varchar", "length": 255 },
"Role": { "type": "varchar", "length": 255 },
"Location": { "type": "varchar", "length": 255 },
"Route": { "type": "varchar", "length": 255 },
"Email": { "type": "varchar", "length": 255 },
"DisplayName": { "type": "varchar", "length": 255 },
"Team": { "type": "varchar", "length": 255 },
"Username": { "type": "varchar", "length": 50 },
"AuthenticatedUsername": { "type": "varchar", "length": 50 },
"UITheme": { "type": "varchar", "length": 50 },
"Bio": { "type": "text" },
"PasswordHash": { "type": "text" },
"Image": { "type": "text" },
"AvatarURL": { "type": "text" },
"SQL_USER": { "type": "boolean" }
}
}

View File

@@ -144,22 +144,43 @@ function generateReadQuery(dbType, table, params) {
// Prepare an array to hold WHERE conditions // Prepare an array to hold WHERE conditions
const whereConditions = []; const whereConditions = [];
// Iterate over each parameter in params
for (const key in params) { for (const key in params) {
if (params.hasOwnProperty(key)) { if (params[key]) {
const value = params[key];
// Construct the condition based on dbType
let condition; let condition;
if (key === 'partialMatch') continue; // Skip the partialSearch flag
switch (dbType.toUpperCase()) { switch (dbType.toUpperCase()) {
case 'MSSQL': case 'MSSQL':
if (params.partialMatch) {
condition = `${key} LIKE '%' + @${key} + '%'`;
params[key] = `%${params[key]}%`; // Add % wildcard in params
} else {
condition = `${key} = @${key}`; condition = `${key} = @${key}`;
}
break;
case 'ORACLE':
if (params.partialMatch) {
condition = `${key} LIKE '%' || :${key} || '%'`;
params[key] = `%${params[key]}%`; // Add % wildcard in params
} else {
condition = `${key} = :${key}`;
}
break; break;
case 'POSTGRES': case 'POSTGRES':
if (params.partialMatch) {
condition = `${key} LIKE $1`;
params[key] = `%${params[key]}%`; // Add % wildcard in params
} else {
condition = `${key} = $1`; condition = `${key} = $1`;
}
break; break;
case 'MARIADB': case 'MARIADB':
if (params.partialMatch) {
condition = `${key} LIKE ?`;
params[key] = `%${params[key]}%`; // Add % wildcard in params
} else {
condition = `${key} = ?`; condition = `${key} = ?`;
}
break; break;
default: default:
throw new Error('Unsupported database type'); throw new Error('Unsupported database type');
@@ -177,7 +198,6 @@ function generateReadQuery(dbType, table, params) {
return query; return query;
} }
/** /**
* Generates a UPDATE SQL query string based on database type, table, and parameters. * Generates a UPDATE SQL query string based on database type, table, and parameters.
* *
@@ -221,8 +241,6 @@ async function generateUpdateQuery(dbType, table, params) {
return `UPDATE ${table} SET ${updateClause} ${whereClause};`; return `UPDATE ${table} SET ${updateClause} ${whereClause};`;
} }
/** /**
* Generates a DELETE SQL query string based on database type, table, and parameters. * Generates a DELETE SQL query string based on database type, table, and parameters.
* *