minervajs-helmet 1.0.12 → 1.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/README.md +214 -47
  2. package/js/db_mysql.js +85 -8
  3. package/package.json +3 -3
  4. package/src/db.js +133 -68
package/README.md CHANGED
@@ -1,64 +1,231 @@
1
- # MinervaJS.Helmet
2
- Modulo para la gestion de las conección a la base de datos, permite conectarse a varios tipos utilizando sobrecarga de metodos, tolera MySQL y Oracle Client
1
+ # MinervaJS-Helmet
3
2
 
4
- Ejemplo: Partiendo de un proyecto NodeJS en blanco recien creado
5
- `$ npm i minervajs-helmet `
3
+ ## 🛡️ Descripción
6
4
 
7
- #### Archivo: ./config/settings.js
8
- Adicionas una Entrada, por cada tipo y base de datos
9
- ```javascript
5
+ **MinervaJS-Helmet** es el módulo encargado de la **gestión unificada de conexiones y operaciones de base de datos** dentro del ecosistema **MinervaJS**.
6
+
7
+ Su función principal es abstraer el motor de base de datos (MySQL, PostgreSQL, Oracle, etc.) y exponer una **API homogénea**, permitiendo que el resto del sistema funcione de forma **JSON-driven**, desacoplada y extensible.
8
+
9
+ ---
10
+
11
+ ## 🎯 Objetivos
12
+
13
+ * Centralizar la gestión de conexiones a bases de datos
14
+ * Soportar múltiples motores de forma transparente
15
+ * Proveer una API común para:
16
+
17
+ * Consultas de lectura (SELECT)
18
+ * Operaciones de escritura (INSERT / UPDATE / DELETE / DDL)
19
+ * Procedimientos almacenados
20
+ * Facilitar la construcción de backends genéricos y dinámicos
21
+
22
+ ---
23
+
24
+ ## 🧱 Arquitectura
25
+
26
+ ```
27
+ MinervaJS
28
+ └── Helmet
29
+ ├── db_mysql.js
30
+ ├── db_postgres.js
31
+ ├── db_oracle.js
32
+ └── connections (cache interno)
33
+ ```
34
+
35
+ Helmet actúa como un **dispatcher**, delegando la ejecución a proveedores específicos que implementan un contrato estándar.
36
+
37
+ ---
38
+
39
+ ## 🔌 Contrato estándar de proveedores
40
+
41
+ Cada proveedor de base de datos debe implementar las siguientes funciones:
42
+
43
+ ```js
44
+ connect(config)
45
+ query(connection, sql, params = [])
46
+ execute(connection, sql, params = [])
47
+ call(connection, procedureName, params = {})
48
+ close(connection)
49
+ closeAll(config)
50
+ ```
51
+
52
+ Este contrato garantiza que Helmet pueda operar sin conocer los detalles del motor subyacente.
53
+
54
+ ---
55
+
56
+ ## 📦 Instalación
57
+
58
+ ```bash
59
+ npm install minervajs-helmet
60
+ ```
61
+
62
+ > *(o incluir el módulo directamente dentro del proyecto MinervaJS)*
63
+
64
+ ---
65
+
66
+ ## ⚙️ Configuración de base de datos
67
+
68
+ Ejemplo de archivo `database.json`:
69
+
70
+ ```json
10
71
  {
11
- 'my_mysql': // 'Perfil de Conexión'
12
- {
13
- type: 'mysql',
14
- host: 'sql3.freesqldatabase.com', // 'localhost',
15
- port: 3306, // 'puerto',
16
- user: 'sql3772729', // 'usuario',
17
- password: 'esUA3qpGKD', // 'contraseña',
18
- database: 'sql3772729' // 'nombre_db'
72
+ "mysqlMain": {
73
+ "type": "mysql",
74
+ "host": "localhost",
75
+ "port": 3306,
76
+ "user": "user",
77
+ "password": "password",
78
+ "database": "minerva"
19
79
  }
20
80
  }
21
81
  ```
22
82
 
23
- #### Archivo: index.js
24
-
25
- ```javascript
26
- var db = require('minervajs-helmet');
27
- var config = require('./config/settings.js'); // Configuración de Conexión
28
-
29
- async function main() {
30
- try {
31
- // Conexión a MySQL
32
- const mysqlConnection = await db.connect('my_mysql', config);
33
- const mysqlResult = await db.query('my_mysql', ' SELECT * FROM test ;', [], config); // Ejemplo sin parámetros
34
- console.log('Resultados de MySQL:', mysqlResult);
35
- await db.close('my_mysql', config);
36
- } catch (error) {
37
- console.error('Error:', error);
38
- }
39
- }
83
+ En la instalacion, puedes hacer uso del archivo muestra que esta en *\node_modules\minervajs-helmet\example\settings.js*
84
+
85
+ ---
86
+
87
+ ## 🚀 Uso básico
40
88
 
41
- main();
89
+ ```js
90
+ const helmet = require('./helmet');
91
+ const config = require('./database.json');
42
92
  ```
43
93
 
44
- #### Salida de resultados
45
- ```javascript
46
- Resultados de MySQL: [
94
+ ---
95
+
96
+ ### 🔍 Consultas de lectura (SELECT)
97
+
98
+ ```js
99
+ const rows = await helmet.query(
100
+ 'mysqlMain',
101
+ 'SELECT * FROM pais WHERE iso3 = ?',
102
+ ['SLV'],
103
+ config
104
+ );
105
+ ```
106
+
107
+ ---
108
+
109
+ ### ✏️ Operaciones de escritura
110
+
111
+ ```js
112
+ const result = await helmet.execute(
113
+ 'mysqlMain',
114
+ 'UPDATE pais SET nombre = ? WHERE iso3 = ?',
115
+ ['El Salvador', 'SLV'],
116
+ config
117
+ );
118
+
119
+ console.log(result.rowsAffected);
120
+ ```
121
+
122
+ ---
123
+
124
+ ### 🧠 Procedimientos almacenados
125
+
126
+ ```js
127
+ const result = await helmet.call(
128
+ 'mysqlMain',
129
+ 'sp_pais_insert',
47
130
  {
48
- id: 1,
49
- codigo: 'SV',
50
- descripcion: 'El Salvador',
51
- fecha: 2025-04-02T06:00:00.000Z
131
+ p_iso3: 'SLV',
132
+ p_nombre: 'El Salvador',
133
+ p_leyenda: 'Centroamérica',
134
+ p_iso2: 'SV',
135
+ p_existe: { out: true }
52
136
  },
53
- {
54
- id: 2,
55
- codigo: 'EU',
56
- descripcion: 'Estados Unidos',
57
- fecha: 2025-04-02T06:00:00.000Z
137
+ config
138
+ );
139
+ ```
140
+
141
+ **Resultado estándar:**
142
+
143
+ ```js
144
+ {
145
+ resultSets: [...],
146
+ out: {
147
+ p_existe: 0
58
148
  }
59
- ]
149
+ }
60
150
  ```
61
151
 
62
- En la instalacion, puedes hacer uso del archivo muestra que esta en *\node_modules\minervajs-helmet\config\settings.js*
152
+ ---
153
+
154
+ ## 🔐 Gestión de conexiones
155
+
156
+ * Una conexión por perfil de base de datos
157
+ * Reutilización automática
158
+ * Cache interno
159
+ * Cierre explícito
160
+
161
+ ```js
162
+ await helmet.close('mysqlMain', config);
163
+ await helmet.closeAll(config);
164
+ ```
165
+
166
+ ---
167
+
168
+ ## ⚠️ Manejo de errores
169
+
170
+ Helmet agrega contexto a los errores:
171
+
172
+ ```
173
+ [Helmet][mysql][execute] Duplicate entry
174
+ ```
175
+
176
+ Esto facilita el logging y el diagnóstico.
177
+
178
+ ---
179
+
180
+ ## 🧩 Integración JSON-driven (MinervaJS)
181
+
182
+ Helmet está diseñado para ejecutarse a partir de manifiestos JSON:
183
+
184
+ ```json
185
+ {
186
+ "database": "mysqlMain",
187
+ "procedure": "sp_pais_insert",
188
+ "params": {
189
+ "p_iso3": "$body.iso3",
190
+ "p_nombre": "$body.nombre",
191
+ "p_existe": { "out": true }
192
+ }
193
+ }
194
+ ```
195
+
196
+ La API ejecuta la operación sin conocer SQL ni lógica de negocio.
197
+
198
+ ---
199
+
200
+ ## ✅ Buenas prácticas
201
+
202
+ * Usar `query()` exclusivamente para SELECT
203
+ * Usar `execute()` para DML / DDL
204
+ * Encapsular lógica compleja en Stored Procedures
205
+ * Cerrar conexiones en shutdown de la aplicación
206
+ * Mantener la configuración desacoplada
207
+
208
+
209
+ ---
210
+
211
+ ## 🔮 Evolución futura
212
+
213
+ * Pooling de conexiones
214
+ * Transacciones (`begin / commit / rollback`)
215
+ * Multi-tenant
216
+ * Logging estructurado
217
+ * Métricas
218
+ * Soporte para nuevos motores
219
+
220
+ ---
221
+
222
+ ## 📌 Conclusión
223
+
224
+ **MinervaJS-Helmet** es el pilar de acceso a datos de MinervaJS.
225
+
226
+ Su diseño modular, homogéneo y desacoplado permite construir aplicaciones dinámicas, escalables y mantenibles, donde la lógica de negocio puede definirse de forma declarativa y evolucionar sin reescribir el backend.
227
+
228
+
229
+
63
230
 
64
231
 
package/js/db_mysql.js CHANGED
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * @module js/mysql
3
3
  * @name Provider DB js/mysql
4
- * @description Módulo para la conexión y operaciones del Proveedor MySQL.
4
+ * @description Módulo proveedor MySQL para MinervaJS-Helmet.
5
+ * Implementa operaciones estándar: query, execute y call.
6
+ *
5
7
  */
6
8
 
7
9
  /**
@@ -34,7 +36,7 @@ async function connect(config)
34
36
  }
35
37
  catch (error)
36
38
  {
37
- console.error('Error al conectar a MySQL:', error);
39
+ console.error('[MySQL][connect]', error.message);
38
40
  throw error; // Re-lanzamos el error para que sea manejado por el llamador
39
41
  }
40
42
  }
@@ -46,23 +48,91 @@ async function connect(config)
46
48
  * @function query
47
49
  * @param {mysql.Connection} connection - Objeto de conexión de MySQL.
48
50
  * @param {string} sql - Consulta SQL.
49
- * @param {Array} [values] - Parámetros para la consulta.
51
+ * @param {Array} [params] - Parámetros para la consulta.
50
52
  * @returns {Promise<Array>} Filas resultantes de la consulta.
51
53
  */
52
- async function query(connection, sql, values = [])
54
+ async function query(connection, sql, params = [])
53
55
  {
54
56
  try
55
57
  {
56
- const [rows, fields] = await connection.execute(sql, values);
58
+ const [rows, fields] = await connection.execute(sql, params);
57
59
  return rows;
58
60
  }
59
61
  catch (error)
60
62
  {
61
- console.error('Error al ejecutar la consulta MySQL:', error);
63
+ console.error('[MySQL][query]', error.message);
62
64
  throw error;
63
65
  }
64
66
  }
65
67
 
68
+
69
+
70
+ /**
71
+ * Ejecuta una sentencia de escritura (INSERT, UPDATE, DELETE, DDL).
72
+ */
73
+ async function execute(connection, sql, params = [])
74
+ {
75
+ try {
76
+ const [result] = await connection.execute(sql, params);
77
+
78
+ return {
79
+ rowsAffected: result.affectedRows,
80
+ insertId: result.insertId || null
81
+ };
82
+ }
83
+ catch (error)
84
+ {
85
+ console.error('[MySQL][execute]', error.message);
86
+ throw error;
87
+ }
88
+ }
89
+
90
+
91
+ /**
92
+ * Ejecuta un procedimiento almacenado.
93
+ *
94
+ * Nota:
95
+ * MySQL maneja OUT params mediante variables de sesión (@var).
96
+ */
97
+ async function call(connection, procedureName, params = {}) {
98
+ try {
99
+ const keys = Object.keys(params);
100
+ const values = [];
101
+
102
+ const placeholders = keys.map(key => {
103
+ if (params[key]?.out) {
104
+ return `@${key}`;
105
+ }
106
+ values.push(params[key]);
107
+ return '?';
108
+ }).join(',');
109
+
110
+ // 1️ - Ejecutar CALL
111
+ const callSQL = `CALL ${procedureName}(${placeholders})`;
112
+ const [resultSets] = await connection.query(callSQL, values);
113
+
114
+ // 2️ - Recuperar OUT params
115
+ const outParams = keys.filter(k => params[k]?.out);
116
+ let out = {};
117
+
118
+ if (outParams.length > 0) {
119
+ const selectOut = `SELECT ${outParams.map(k => `@${k} AS ${k}`).join(',')}`;
120
+ const [rows] = await connection.query(selectOut);
121
+ out = rows[0];
122
+ }
123
+
124
+ return {
125
+ resultSets,
126
+ out
127
+ };
128
+ } catch (error) {
129
+ console.error('[MySQL][call]', error.message);
130
+ throw error;
131
+ }
132
+ }
133
+
134
+
135
+
66
136
  /**
67
137
  * Cierra la conexión a la base de datos MySQL.
68
138
  *
@@ -77,9 +147,16 @@ async function close(connection)
77
147
  { await connection.end(); }
78
148
  catch (error)
79
149
  {
80
- console.error('Error al cerrar la conexión MySQL:', error);
150
+ console.error('[MySQL][close]', error.message);
81
151
  throw error;
82
152
  }
83
153
  }
84
154
 
85
- module.exports = { connect, query, close };
155
+ module.exports =
156
+ {
157
+ connect,
158
+ query,
159
+ execute,
160
+ call,
161
+ close
162
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minervajs-helmet",
3
- "version": "1.0.12",
3
+ "version": "1.0.15",
4
4
  "main": "src/db.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -11,8 +11,8 @@
11
11
  "connect",
12
12
  "MySQL",
13
13
  "Oracle Client",
14
- "PostGres",
15
- "MinervaJS"
14
+ "PostGres",
15
+ "MinervaJS"
16
16
  ],
17
17
  "author": "alexander.enrique.escobar@gmail.com",
18
18
  "license": "BSD-2-Clause",
package/src/db.js CHANGED
@@ -2,8 +2,7 @@
2
2
  *
3
3
  * @name MinervaJS-Helmet
4
4
  * @module MinervaJS-Helmet
5
- * @description Modulo gestor de la coneccion a la base de datos, con capacidad de realizar consultas en múltiples bases de datos.
6
- *
5
+ * @description Gestor unificado de conexiones y ejecución de operaciones en múltiples motores de bases de datos.
7
6
  */
8
7
 
9
8
  const postgres = require('../js/db_postgres');
@@ -60,6 +59,38 @@ async function connect(databaseName, config)
60
59
  return connections[databaseName];
61
60
  }
62
61
 
62
+
63
+ /**
64
+ * Dispatcher interno para query / execute / call
65
+ */
66
+ async function run(databaseName, target, params, config, mode)
67
+ {
68
+ const connection = await connect(databaseName, config);
69
+ const dbConfig = config[databaseName];
70
+
71
+ try {
72
+ switch (dbConfig.type) {
73
+ case 'postgres':
74
+ return await postgres[mode](connection, target, params);
75
+
76
+ case 'mysql':
77
+ return await mysql[mode](connection, target, params);
78
+
79
+ case 'oracle':
80
+ return await oracle[mode](connection, target, params);
81
+
82
+ default:
83
+ throw new Error(`Tipo de base de datos '${dbConfig.type}' no soportado.`);
84
+ }
85
+ }
86
+ catch (err)
87
+ {
88
+ throw new Error(`[Helmet][${dbConfig.type}][${mode}] ${err.message}`);
89
+ }
90
+ }
91
+
92
+
93
+
63
94
  /**
64
95
  * Ejecuta una consulta en la base de datos especificada.
65
96
  *
@@ -73,29 +104,54 @@ async function connect(databaseName, config)
73
104
  * @throws {Error} Si el tipo de base de datos no es soportado para la operación 'query'.
74
105
  * @description Ejecuta una sentencia SQL en la base de datos especificada y devuelve un objeto en un set de datos
75
106
  */
76
- async function query(databaseName, sql, values = [], config)
107
+ // async function query(databaseName, sql, values = [], config)
108
+ // {
109
+ // const connection = await connect(databaseName, config);
110
+ // const dbConfig = config[databaseName];
111
+
112
+ // switch (dbConfig.type)
113
+ // {
114
+ // case 'postgres':
115
+ // return postgres.query(connection, sql);
116
+ // case 'mysql':
117
+ // return mysql.query(connection, sql, values);
118
+ // // case 'mariadb': // agregamos el caso MariaDB
119
+ // // return mariadb.query(connection, sql, binds);
120
+ // // break;
121
+ // case 'oracle':
122
+ // return oracle.query(connection, sql);
123
+ // // case 'mongodb':
124
+ // // // Adaptar la consulta SQL a la sintaxis de MongoDB
125
+ // // console.warn("La función 'query' no es directamente aplicable a MongoDB con sintaxis SQL.");
126
+ // // return null; // O lanzar un error
127
+ // default:
128
+ // throw new Error(`Tipo de base de datos '${dbConfig.type}' no soportado para la operación 'query'.`);
129
+ // }
130
+ // }
131
+
132
+ /**
133
+ * Ejecuta consultas de lectura (SELECT).
134
+ */
135
+ async function query(databaseName, sql, params = [], config)
77
136
  {
78
- const connection = await connect(databaseName, config);
79
- const dbConfig = config[databaseName];
137
+ return run(databaseName, sql, params, config, 'query');
138
+ }
80
139
 
81
- switch (dbConfig.type)
82
- {
83
- case 'postgres':
84
- return postgres.query(connection, sql);
85
- case 'mysql':
86
- return mysql.query(connection, sql, values);
87
- // case 'mariadb': // agregamos el caso MariaDB
88
- // return mariadb.query(connection, sql, binds);
89
- // break;
90
- case 'oracle':
91
- return oracle.query(connection, sql);
92
- // case 'mongodb':
93
- // // Adaptar la consulta SQL a la sintaxis de MongoDB
94
- // console.warn("La función 'query' no es directamente aplicable a MongoDB con sintaxis SQL.");
95
- // return null; // O lanzar un error
96
- default:
97
- throw new Error(`Tipo de base de datos '${dbConfig.type}' no soportado para la operación 'query'.`);
98
- }
140
+ /**
141
+ * Ejecuta sentencias de escritura (INSERT, UPDATE, DELETE, DDL).
142
+ */
143
+ async function execute(databaseName, sql, params = [], config)
144
+ {
145
+ return run(databaseName, sql, params, config, 'execute');
146
+ }
147
+
148
+
149
+ /**
150
+ * Ejecuta procedimientos almacenados o funciones.
151
+ */
152
+ async function call(databaseName, procedureName, params = {}, config)
153
+ {
154
+ return run(databaseName, procedureName, params, config, 'call');
99
155
  }
100
156
 
101
157
  /**
@@ -110,28 +166,28 @@ async function query(databaseName, sql, values = [], config)
110
166
  */
111
167
  async function close(databaseName, config)
112
168
  {
113
- if (connections[databaseName])
114
- {
115
- const dbConfig = config[databaseName];
116
- switch (dbConfig.type) {
117
- case 'postgres':
118
- await postgres.close(connections[databaseName]);
119
- break;
120
- case 'mysql':
121
- await mysql.close(connections[databaseName]);
122
- break;
123
- // case 'mariadb': // agregamos el caso MariaDB
124
- // await mariadb.close(connections[databaseName]);
125
- // break;
126
- case 'oracle':
127
- await oracle.close(connections[databaseName]);
128
- break;
129
- // case 'mongodb':
130
- // await mongodb.close(connections[databaseName]);
131
- // break;
132
- }
133
- delete connections[databaseName];
169
+ if (!connections[databaseName]) return;
170
+
171
+ const dbConfig = config[databaseName];
172
+ switch (dbConfig.type) {
173
+ case 'postgres':
174
+ await postgres.close(connections[databaseName]);
175
+ break;
176
+ case 'mysql':
177
+ await mysql.close(connections[databaseName]);
178
+ break;
179
+ // case 'mariadb': // agregamos el caso MariaDB
180
+ // await mariadb.close(connections[databaseName]);
181
+ // break;
182
+ case 'oracle':
183
+ await oracle.close(connections[databaseName]);
184
+ break;
185
+ // case 'mongodb':
186
+ // await mongodb.close(connections[databaseName]);
187
+ // break;
134
188
  }
189
+ delete connections[databaseName];
190
+
135
191
  }
136
192
 
137
193
 
@@ -144,38 +200,47 @@ async function close(databaseName, config)
144
200
  * @returns {result} result/err - Devuelve un objeto con el set de datos o un objeto err con la respuesta del error
145
201
  * @description Ejecuta una sentencia SQL y devuelve un objeto en un set de datos
146
202
  */
147
- async function execute(databaseName, sql, values = [], config)
148
- {
149
- const connection = await connect(databaseName, config);
150
- const dbConfig = config[databaseName];
203
+ // async function execute(databaseName, sql, values = [], config)
204
+ // {
205
+ // const connection = await connect(databaseName, config);
206
+ // const dbConfig = config[databaseName];
151
207
 
152
- switch (dbConfig.type)
153
- {
154
- case 'postgres':
155
- return postgres.query(connection, sql);
156
- case 'mysql':
157
- return mysql.query(connection, sql, values);
158
- // case 'mariadb': // agregamos el caso MariaDB
159
- // return mariadb.query(connection, sql, binds);
160
- // break;
161
- case 'oracle':
162
- return oracle.query(connection, sql);
163
- // case 'mongodb':
164
- // // Adaptar la consulta SQL a la sintaxis de MongoDB
165
- // console.warn("La función 'query' no es directamente aplicable a MongoDB con sintaxis SQL.");
166
- // return null; // O lanzar un error
167
- default:
168
- throw new Error(`Tipo de base de datos '${dbConfig.type}' no soportado para la operación 'query'.`);
169
- }
170
- }
208
+ // switch (dbConfig.type)
209
+ // {
210
+ // case 'postgres':
211
+ // return postgres.query(connection, sql);
212
+ // case 'mysql':
213
+ // return mysql.query(connection, sql, values);
214
+ // // case 'mariadb': // agregamos el caso MariaDB
215
+ // // return mariadb.query(connection, sql, binds);
216
+ // // break;
217
+ // case 'oracle':
218
+ // return oracle.query(connection, sql);
219
+ // // case 'mongodb':
220
+ // // // Adaptar la consulta SQL a la sintaxis de MongoDB
221
+ // // console.warn("La función 'query' no es directamente aplicable a MongoDB con sintaxis SQL.");
222
+ // // return null; // O lanzar un error
223
+ // default:
224
+ // throw new Error(`Tipo de base de datos '${dbConfig.type}' no soportado para la operación 'query'.`);
225
+ // }
226
+ // }
171
227
 
172
-
228
+ /**
229
+ * Cierra todas las conexiones activas.
230
+ */
231
+ async function closeAll(config)
232
+ {
233
+ for (const dbName of Object.keys(connections))
234
+ { await close(dbName, config); }
235
+ }
173
236
 
174
237
  module.exports =
175
238
  {
176
239
  connect,
177
240
  query,
178
241
  execute,
242
+ call,
179
243
  close,
244
+ closeAll
180
245
  // ... otras funciones comunes
181
246
  };