Compare commits
1 Commits
integratio
...
dependabot
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c8ab7c9d8 |
87
client.js
Normal file
87
client.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// client.js
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
const generateUUID = require('uuid').v4;
|
||||||
|
async function testQuery(table, operation, params) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('http://localhost:3100/api/query', {
|
||||||
|
table,
|
||||||
|
operation,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
console.log(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error executing query:', error.response ? error.response.data : error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage:
|
||||||
|
(async () => {
|
||||||
|
// CREATE: Ensure all required fields are present
|
||||||
|
const ID1 = generateUUID();
|
||||||
|
const ID2 = generateUUID();
|
||||||
|
const ID3 = generateUUID();
|
||||||
|
const ID4 = generateUUID();
|
||||||
|
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created with ID: ' + ID1,
|
||||||
|
ID: ID1, // Assuming an ID is needed for identification or to prevent conflicts
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created with ID: ' + ID2,
|
||||||
|
ID: ID2, // Assuming an ID is needed for identification or to prevent conflicts
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created wth ID: ' + ID3,
|
||||||
|
ID: ID3, // Assuming an ID is needed for identification or to prevent conflicts
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created with ID: ' + ID4,
|
||||||
|
ID: ID4, // Assuming an ID is needed for identification or to prevent conflicts
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
|
||||||
|
// UPDATE: Ensure both the ID and at least one field to update are present. ID must be the LAST field.
|
||||||
|
await testQuery('Test', 'UPDATE', {
|
||||||
|
Name: 'Tested item name',
|
||||||
|
Text: 'Updated item text successfully for item: ' + ID2,
|
||||||
|
ID: ID2
|
||||||
|
});
|
||||||
|
|
||||||
|
// READ: Ensure the ID is present
|
||||||
|
await testQuery('Test', 'READ', {
|
||||||
|
ID : ID1
|
||||||
|
} );
|
||||||
|
|
||||||
|
// READ: See all rows in a table
|
||||||
|
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
|
||||||
|
await testQuery('Test', 'DELETE', {
|
||||||
|
ID: ID3
|
||||||
|
});
|
||||||
|
/*
|
||||||
|
// COMPARE: Ensure both compareTable and idField are present
|
||||||
|
const compareParams = {
|
||||||
|
compareTable: 'Test2',
|
||||||
|
idField: 'ID',
|
||||||
|
ID: '1'
|
||||||
|
};
|
||||||
|
await testQuery('Test', 'COMPARE', compareParams);*/
|
||||||
|
})();
|
||||||
2162
package-lock.json
generated
Normal file
2162
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
12
package.json
Normal file
12
package.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.13.5",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^17.2.1",
|
||||||
|
"express": "^5.1.0",
|
||||||
|
"mssql": "^11.0.1",
|
||||||
|
"mysql2": "^3.14.3",
|
||||||
|
"oracledb": "^6.9.0",
|
||||||
|
"pg": "^8.16.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"mssql": {
|
|
||||||
"ID": { "type": "uniqueidentifier" },
|
|
||||||
"Name": { "type": "nvarchar", "maxLength": 255 },
|
|
||||||
"Description": { "type": "nvarchar", "maxLength": 255 },
|
|
||||||
"Building": { "type": "nvarchar", "maxLength": 255 },
|
|
||||||
"Bin": { "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": "nvarchar", "maxlength": 255 },
|
|
||||||
"PasswordHash": { "type": "text" },
|
|
||||||
"Image": { "type": "nvarchar", "maxLength": 255 },
|
|
||||||
"AvatarURL": { "type": "nvarchar", "maxlength": 255 },
|
|
||||||
"SQL_USER": { "type": "bit" },
|
|
||||||
"Text": { "type": "nvarchar", "maxLength": 255 },
|
|
||||||
"Quantity": { "type": "int" },
|
|
||||||
"RequestPayload": { "type": "text" }
|
|
||||||
},
|
|
||||||
"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" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
134
readme.md
Normal file
134
readme.md
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
## JS DB Connector Service Guide
|
||||||
|
This set of modules is intended to serve as a general purpose toolkit for building Node.js applications with a database-type agnostic SQL backend. The audience at creation is myself, as I wanted a reusable module for communicating with my SQL backends in Node.js projects.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
At the moment, CRUD operations against Microsoft SQL Server and MariaDB servers are supported. I plan to implement support for interoperability with Postgres, Oracle, and MongoDB as well to provide the most possible utility in this package. An example implementation server.js:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// server.js
|
||||||
|
|
||||||
|
require('dotenv').config();
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const queryExecutor = require('./services/dbconnector/queryExecutor');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(cors());
|
||||||
|
/**
|
||||||
|
* Example route to demonstrate CRUD operations.
|
||||||
|
*/
|
||||||
|
app.post('/api/query', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { table, operation, params } = req.body;
|
||||||
|
const result = await queryExecutor.executeQuery(table, operation, params);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error executing query:', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3100;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on port ${PORT}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Additionally, here's an example client.js script to test the database connector:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// client.js
|
||||||
|
|
||||||
|
const axios = require('axios');
|
||||||
|
const generateUUID = require('uuid').v4;
|
||||||
|
async function testQuery(table, operation, params) {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('http://localhost:3100/api/query', {
|
||||||
|
table,
|
||||||
|
operation,
|
||||||
|
params
|
||||||
|
});
|
||||||
|
console.log(response.data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error executing query:', error.response ? error.response.data : error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage:
|
||||||
|
(async () => {
|
||||||
|
// CREATE: Ensure all required fields are present
|
||||||
|
const ID1 = generateUUID();
|
||||||
|
const ID2 = generateUUID();
|
||||||
|
const ID3 = generateUUID();
|
||||||
|
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created with ID: ' + ID1,
|
||||||
|
ID: ID1
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created with ID: ' + ID2,
|
||||||
|
ID: ID2
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created wth ID: ' + ID3,
|
||||||
|
ID: ID3
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
await testQuery('Test', 'CREATE', {
|
||||||
|
Name: 'Test Item',
|
||||||
|
Text: 'A sample item created with ID: ' + ID4,
|
||||||
|
ID: ID4
|
||||||
|
// Add other necessary fields here based on your schema
|
||||||
|
});
|
||||||
|
|
||||||
|
// UPDATE: Ensure both the ID and at least one field to update are present. ID must be the LAST field.
|
||||||
|
await testQuery('Test', 'UPDATE', {
|
||||||
|
Name: 'Tested item name',
|
||||||
|
Text: 'Updated item text successfully for item: ' + ID2,
|
||||||
|
ID: ID2
|
||||||
|
});
|
||||||
|
|
||||||
|
// READ: Ensure the ID is present
|
||||||
|
await testQuery('Test', 'READ', {
|
||||||
|
ID : ID1
|
||||||
|
} );
|
||||||
|
|
||||||
|
// READ: See all rows in a table
|
||||||
|
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
|
||||||
|
await testQuery('Test', 'DELETE', {
|
||||||
|
ID: ID3
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
The following environment variables are used to control the operation of the DB connector:
|
||||||
|
```
|
||||||
|
DB_USER= # A user with permissions to manage the database
|
||||||
|
DB_PASSWORD= # Password for DB_USER
|
||||||
|
DB_SERVER= # Hostname or IP of the database server
|
||||||
|
DB_PORT= # Port if not using default for the database type
|
||||||
|
DB_DATABASE= # Database to be used. As written, only a single database is supported
|
||||||
|
DB_TYPE= # MSSQL and MARIADB are implemented. ORACLEDB and POSTGRES are planned.
|
||||||
|
DB_TRUST_CERT= # Trust the local system SSL certificate, useful for developing against MSSQL server.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
To test the function of these modules, run "node server.js" in the project root, and post to http://localhost:3100/api/query. You can instead run client.js to automatically attempt each query type against the default server URL.
|
||||||
28
server.js
Normal file
28
server.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
// server.js
|
||||||
|
|
||||||
|
require('dotenv').config();
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
const queryExecutor = require('./services/dbconnector/queryExecutor');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(cors());
|
||||||
|
/**
|
||||||
|
* Example route to demonstrate CRUD operations.
|
||||||
|
*/
|
||||||
|
app.post('/api/query', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { table, operation, params } = req.body;
|
||||||
|
const result = await queryExecutor.executeQuery(table, operation, params);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error executing query:', err);
|
||||||
|
res.status(500).json({ error: err.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const PORT = process.env.PORT || 3100;
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`Server is running on port ${PORT}`);
|
||||||
|
});
|
||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
require('dotenv').config();
|
require('dotenv').config();
|
||||||
const sql = require('mssql');
|
const sql = require('mssql');
|
||||||
|
const oracledb = require('oracledb');
|
||||||
const mysql = require('mysql2/promise');
|
const mysql = require('mysql2/promise');
|
||||||
|
const { Client } = require('pg');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a connection based on the configured database type.
|
* Gets a connection based on the configured database type.
|
||||||
@@ -16,8 +18,14 @@ async function getConnection() {
|
|||||||
switch (dbType) {
|
switch (dbType) {
|
||||||
case 'MSSQL':
|
case 'MSSQL':
|
||||||
return await sql.connect(config);
|
return await sql.connect(config);
|
||||||
|
case 'ORACLE':
|
||||||
|
return await oracledb.getConnection(config);
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
return await mysql.createConnection(config);
|
return await mysql.createConnection(config);
|
||||||
|
case 'POSTGRES':
|
||||||
|
const client = new Client(config);
|
||||||
|
await client.connect();
|
||||||
|
return client;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unsupported database type');
|
throw new Error('Unsupported database type');
|
||||||
}
|
}
|
||||||
@@ -42,6 +50,12 @@ function getDBConfig(dbType) {
|
|||||||
trustServerCertificate: true
|
trustServerCertificate: true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
case 'ORACLE':
|
||||||
|
return {
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
connectString: process.env.ORACLE_CONNECTION_STRING
|
||||||
|
};
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
return {
|
return {
|
||||||
host: process.env.DB_SERVER,
|
host: process.env.DB_SERVER,
|
||||||
@@ -51,6 +65,14 @@ function getDBConfig(dbType) {
|
|||||||
database: process.env.DB_DATABASE,
|
database: process.env.DB_DATABASE,
|
||||||
namedPlaceholders: true
|
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:
|
default:
|
||||||
throw new Error('Unsupported database type');
|
throw new Error('Unsupported database type');
|
||||||
}
|
}
|
||||||
@@ -28,11 +28,15 @@ async function formatQueryParams(params) {
|
|||||||
case 'MSSQL':
|
case 'MSSQL':
|
||||||
formattedParams = formatMSSQLParams(params, paramsConfig.mssql);
|
formattedParams = formatMSSQLParams(params, paramsConfig.mssql);
|
||||||
break;
|
break;
|
||||||
|
case 'ORACLE':
|
||||||
|
formattedParams = formatOracleParams(params, paramsConfig.oracle);
|
||||||
|
break;
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
formattedParams = formatMariaDBParams(params, paramsConfig.mariaDB);
|
formattedParams = formatMariaDBParams(params, paramsConfig.mariaDB);
|
||||||
break;
|
break;
|
||||||
|
case 'POSTGRES':
|
||||||
|
formattedParams = formatPostgresParams(params, paramsConfig.postgres);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unsupported database type');
|
throw new Error('Unsupported database type');
|
||||||
}
|
}
|
||||||
@@ -87,6 +91,51 @@ function formatMSSQLParams(params, config) {
|
|||||||
return formattedParams;
|
return formattedParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats query parameters for Oracle.
|
||||||
|
*
|
||||||
|
* @param {Object} params - An object containing the parameters to format.
|
||||||
|
* @param {Object} config - Database-specific configuration from JSON.
|
||||||
|
* @returns {Object} Formatted parameters.
|
||||||
|
*/
|
||||||
|
function formatOracleParams(params, config) {
|
||||||
|
const formattedParams = {};
|
||||||
|
for (const key in params) {
|
||||||
|
if (!config[key]) {
|
||||||
|
console.warn(`Parameter '${key}' not defined in the configuration.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats query parameters for MariaDB.
|
* Formats query parameters for MariaDB.
|
||||||
@@ -137,5 +186,51 @@ function formatMariaDBParams(params, config) {
|
|||||||
return formattedParams;
|
return formattedParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats query parameters for PostgreSQL.
|
||||||
|
*
|
||||||
|
* @param {Object} params - An object containing the parameters to format.
|
||||||
|
* @param {Object} config - Database-specific configuration from JSON.
|
||||||
|
* @returns {Object} Formatted parameters.
|
||||||
|
*/
|
||||||
|
function formatPostgresParams(params, config) {
|
||||||
|
const formattedParams = {};
|
||||||
|
for (const key in params) {
|
||||||
|
if (!config[key]) {
|
||||||
|
console.warn(`Parameter '${key}' not defined in the configuration.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = { formatQueryParams };
|
module.exports = { formatQueryParams };
|
||||||
|
|
||||||
93
services/dbconnector/paramsConfig.json
Normal file
93
services/dbconnector/paramsConfig.json
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
{
|
||||||
|
"mssql": {
|
||||||
|
"ID": { "type": "uniqueidentifier" },
|
||||||
|
"Name": { "type": "nvarchar", "maxLength": 255 },
|
||||||
|
"Description": { "type": "nvarchar", "maxLength": 255 },
|
||||||
|
"Building": { "type": "nvarchar", "maxLength": 255 },
|
||||||
|
"Bin": { "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": "nvarchar", "maxlength": 255 },
|
||||||
|
"PasswordHash": { "type": "text" },
|
||||||
|
"Image": { "type": "nvarchar", "maxLength": 255 },
|
||||||
|
"AvatarURL": { "type": "nvarchar", "maxlength": 255 },
|
||||||
|
"SQL_USER": { "type": "bit" },
|
||||||
|
"Text": { "type": "nvarchar", "maxLength": 255 },
|
||||||
|
"Quantity": { "type": "int" },
|
||||||
|
"RequestPayload": { "type": "text" }
|
||||||
|
},
|
||||||
|
"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" }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -117,9 +117,14 @@ function generateCreateQuery(dbType, table, params) {
|
|||||||
.map(key => `@${key}`)
|
.map(key => `@${key}`)
|
||||||
.join(', ');
|
.join(', ');
|
||||||
break;
|
break;
|
||||||
|
case 'POSTGRES':
|
||||||
|
placeholders = Object.keys(params).map(() => '?').join(', '); // Use ? for PostgreSQL
|
||||||
|
break;
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
placeholders = Object.keys(params).map(() => `?`).join(', '); // Use ? for MariaDB
|
placeholders = Object.keys(params).map(() => `?`).join(', '); // Use ? for MariaDB
|
||||||
break;
|
break;
|
||||||
|
case 'ORACLE':
|
||||||
|
throw new Error('CREATE operation is not supported for ORACLE in this implementation.');
|
||||||
default:
|
default:
|
||||||
throw new Error('Unsupported database type for this operation!');
|
throw new Error('Unsupported database type for this operation!');
|
||||||
}
|
}
|
||||||
@@ -156,6 +161,22 @@ function generateReadQuery(dbType, table, params) {
|
|||||||
condition = `${key} = @${key}`;
|
condition = `${key} = @${key}`;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'ORACLE':
|
||||||
|
if (params.partialMatch) {
|
||||||
|
condition = `${key} LIKE '%' || :${key} || '%'`;
|
||||||
|
params[key] = `%${params[key]}%`; // Add % wildcard in params
|
||||||
|
} else {
|
||||||
|
condition = `${key} = :${key}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'POSTGRES':
|
||||||
|
if (params.partialMatch) {
|
||||||
|
condition = `${key} LIKE $1`;
|
||||||
|
params[key] = `%${params[key]}%`; // Add % wildcard in params
|
||||||
|
} else {
|
||||||
|
condition = `${key} = $1`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
if (params.partialMatch) {
|
if (params.partialMatch) {
|
||||||
condition = `${key} LIKE ?`;
|
condition = `${key} LIKE ?`;
|
||||||
@@ -210,6 +231,9 @@ async function generateUpdateQuery(dbType, table, params) {
|
|||||||
case 'MSSQL':
|
case 'MSSQL':
|
||||||
whereClause = `WHERE ID = @ID`;
|
whereClause = `WHERE ID = @ID`;
|
||||||
break;
|
break;
|
||||||
|
case 'POSTGRES':
|
||||||
|
whereClause = `WHERE ID = $2`;
|
||||||
|
break;
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
whereClause = `WHERE ID = ?`;
|
whereClause = `WHERE ID = ?`;
|
||||||
break;
|
break;
|
||||||
@@ -234,8 +258,12 @@ async function generateDeleteQuery(dbType, table, params) {
|
|||||||
switch (dbType.toUpperCase()) {
|
switch (dbType.toUpperCase()) {
|
||||||
case 'MSSQL':
|
case 'MSSQL':
|
||||||
return `DELETE FROM ${table} WHERE ID = @ID;`;
|
return `DELETE FROM ${table} WHERE ID = @ID;`;
|
||||||
|
case 'POSTGRES':
|
||||||
|
return `DELETE FROM ${table} WHERE ID = $1;`; // Use $1 for PostgreSQL
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
return `DELETE FROM ${table} WHERE ID = ?;`;
|
return `DELETE FROM ${table} WHERE ID = ?;`;
|
||||||
|
case 'ORACLE':
|
||||||
|
throw new Error('DELETE operation is not supported for ORACLE in this implementation.');
|
||||||
default:
|
default:
|
||||||
throw new Error('Unsupported database type!');
|
throw new Error('Unsupported database type!');
|
||||||
}
|
}
|
||||||
@@ -251,10 +279,57 @@ async function generateTestQuery(dbType) {
|
|||||||
switch (dbType.toUpperCase()) {
|
switch (dbType.toUpperCase()) {
|
||||||
case 'MSSQL':
|
case 'MSSQL':
|
||||||
return `SELECT @@SPID;`;
|
return `SELECT @@SPID;`;
|
||||||
|
case 'POSTGRES':
|
||||||
|
return `SELECT pg_backend_pid();`; // Use $1 for PostgreSQL
|
||||||
case 'MARIADB':
|
case 'MARIADB':
|
||||||
return `SELECT CONNECTION_ID();`;
|
return `SELECT CONNECTION_ID();`;
|
||||||
|
case 'ORACLE':
|
||||||
|
return `SELECT SYS_CONTEXT('USERENV', 'SESSIONID') FROM dual;`;
|
||||||
default:
|
default:
|
||||||
throw new Error('Unsupported database type!');
|
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 };
|
module.exports = { generateQuery };
|
||||||
@@ -32,11 +32,21 @@ async function executeQuery(table, operation, params) {
|
|||||||
console.log('Formatted parameters for MSSQL:', formattedParams); // Log the formatted parameters
|
console.log('Formatted parameters for MSSQL:', formattedParams); // Log the formatted parameters
|
||||||
result = await executeMSSQLQuery(connection, query, formattedParams);
|
result = await executeMSSQLQuery(connection, query, formattedParams);
|
||||||
break;
|
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':
|
case 'MARIADB':
|
||||||
console.log('Executing MariaDB query:', query); // Debugging log
|
console.log('Executing MariaDB query:', query); // Debugging log
|
||||||
console.log('Formatted parameters for MariaDB:', formattedParams); // Log the formatted parameters
|
console.log('Formatted parameters for MariaDB:', formattedParams); // Log the formatted parameters
|
||||||
result = await executeMariaDBQuery(connection, query, formattedParams);
|
result = await executeMariaDBQuery(connection, query, formattedParams);
|
||||||
break;
|
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:
|
default:
|
||||||
throw new Error('Unsupported database type');
|
throw new Error('Unsupported database type');
|
||||||
}
|
}
|
||||||
@@ -81,6 +91,30 @@ async function executeMSSQLQuery(connection, query, params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
async function executeMariaDBQuery(connection, query, params) {
|
||||||
try {
|
try {
|
||||||
// Extract parameter values in order
|
// Extract parameter values in order
|
||||||
@@ -96,4 +130,20 @@ async function executeMariaDBQuery(connection, query, params) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 };
|
module.exports = { executeQuery };
|
||||||
Reference in New Issue
Block a user