drapi 1.8.51 → 1.8.53
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.
- package/api/basecontrollers/restcontroller.js +2 -1
- package/lib/trigger.js +13 -1
- package/package.json +2 -2
- package/test/trigger.test.js +151 -0
- package/.vscode-upload.json +0 -21
package/lib/trigger.js
CHANGED
|
@@ -30,7 +30,19 @@ module.exports.executeTriggers = async function(ctx,modelName,when,event,data){
|
|
|
30
30
|
await axios.post(url, {data: data},{headers: headers})
|
|
31
31
|
break;
|
|
32
32
|
case 'after':
|
|
33
|
-
|
|
33
|
+
// Sin await a propósito: la respuesta no espera al trigger. Sin el catch, un
|
|
34
|
+
// endpoint caído deja la promesa sin manejar y Node termina el proceso (EPCMS-10295).
|
|
35
|
+
// No se registra data ni headers: llevan datos personales y la apikey.
|
|
36
|
+
axios.post(url, {data: data},{headers: headers}).catch(error => {
|
|
37
|
+
console.error('[drapi] trigger after falló', {
|
|
38
|
+
model: modelName,
|
|
39
|
+
event: event,
|
|
40
|
+
url: url,
|
|
41
|
+
wp: ctx.headers.wp,
|
|
42
|
+
status: error.response ? error.response.status : undefined,
|
|
43
|
+
message: error.message
|
|
44
|
+
});
|
|
45
|
+
});
|
|
34
46
|
break;
|
|
35
47
|
}
|
|
36
48
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drapi",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.53",
|
|
4
4
|
"description": "Dynamic rest api using mongoDb",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "node --test test/"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
// Pruebas de lib/trigger.js (EPCMS-10295). Sin dependencias: node:test y assert.
|
|
2
|
+
// axios se sustituye sobre el módulo cacheado, que es el mismo objeto que
|
|
3
|
+
// trigger.js obtiene con require("axios").
|
|
4
|
+
const nodeTest = require('node:test');
|
|
5
|
+
|
|
6
|
+
// Los casos corren en serie dentro de un solo test: los dobles son globales
|
|
7
|
+
// (axios, console.error) y en Node 16 los test() de primer nivel se solapan.
|
|
8
|
+
const casos = [];
|
|
9
|
+
const test = (nombre, fn) => casos.push([nombre, fn]);
|
|
10
|
+
const assert = require('node:assert');
|
|
11
|
+
const axios = require('axios');
|
|
12
|
+
const trigger = require('../lib/trigger');
|
|
13
|
+
|
|
14
|
+
const ctx = {
|
|
15
|
+
headers: { wp: 'demo', apikey: 'secreta' },
|
|
16
|
+
request: { origin: 'http://persona' }
|
|
17
|
+
};
|
|
18
|
+
const data = { _id: 'p1', nombres: 'Ana', numerodocumento: '1234' };
|
|
19
|
+
|
|
20
|
+
const conTriggers = (triggers, fn) => async () => {
|
|
21
|
+
const originalGet = trigger.getTriggers;
|
|
22
|
+
const originalPost = axios.post;
|
|
23
|
+
const originalError = console.error;
|
|
24
|
+
const errores = [];
|
|
25
|
+
const rechazos = [];
|
|
26
|
+
const alRechazo = (razon) => rechazos.push(razon);
|
|
27
|
+
trigger.getTriggers = async () => triggers;
|
|
28
|
+
// Solo se capturan los mensajes de drapi: Node 16 escribe su ExperimentalWarning
|
|
29
|
+
// del test runner por console.error la primera vez.
|
|
30
|
+
console.error = (...args) => (String(args[0]).startsWith('[drapi]') ? errores.push(args) : originalError(...args));
|
|
31
|
+
process.on('unhandledRejection', alRechazo);
|
|
32
|
+
try {
|
|
33
|
+
await fn({ errores, rechazos });
|
|
34
|
+
} finally {
|
|
35
|
+
trigger.getTriggers = originalGet;
|
|
36
|
+
axios.post = originalPost;
|
|
37
|
+
console.error = originalError;
|
|
38
|
+
process.removeListener('unhandledRejection', alRechazo);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Deja correr los microtasks y un ciclo del event loop para que un rechazo
|
|
43
|
+
// sin manejar alcance a dispararse.
|
|
44
|
+
const esperar = () => new Promise((resolve) => setImmediate(resolve));
|
|
45
|
+
|
|
46
|
+
const errorHttp = (status) => {
|
|
47
|
+
const error = new Error(`Request failed with status code ${status}`);
|
|
48
|
+
error.response = { status, data: { error: 'Unauthorized' } };
|
|
49
|
+
return error;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
test('after: un 401 del endpoint se registra y no queda rechazo sin manejar', conTriggers(
|
|
53
|
+
[{ endpoint: 'http://payroll-interno/colaborador/personaSyncUpdate' }],
|
|
54
|
+
async ({ errores, rechazos }) => {
|
|
55
|
+
axios.post = () => Promise.reject(errorHttp(401));
|
|
56
|
+
const resultado = await trigger.executeTriggers(ctx, 'persona', 'after', 'update', data);
|
|
57
|
+
await esperar();
|
|
58
|
+
assert.strictEqual(resultado, true);
|
|
59
|
+
assert.strictEqual(rechazos.length, 0);
|
|
60
|
+
assert.strictEqual(errores.length, 1);
|
|
61
|
+
const [mensaje, detalle] = errores[0];
|
|
62
|
+
assert.strictEqual(mensaje, '[drapi] trigger after falló');
|
|
63
|
+
assert.deepStrictEqual(detalle, {
|
|
64
|
+
model: 'persona',
|
|
65
|
+
event: 'update',
|
|
66
|
+
url: 'http://payroll-interno/colaborador/personaSyncUpdate',
|
|
67
|
+
wp: 'demo',
|
|
68
|
+
status: 401,
|
|
69
|
+
message: 'Request failed with status code 401'
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
));
|
|
73
|
+
|
|
74
|
+
test('after: un error de DNS (sin response) se registra sin status', conTriggers(
|
|
75
|
+
[{ endpoint: 'http://payroll/colaborador/personaSyncUpdate' }],
|
|
76
|
+
async ({ errores, rechazos }) => {
|
|
77
|
+
axios.post = () => Promise.reject(new Error('getaddrinfo ENOTFOUND payroll'));
|
|
78
|
+
await trigger.executeTriggers(ctx, 'persona', 'after', 'update', data);
|
|
79
|
+
await esperar();
|
|
80
|
+
assert.strictEqual(rechazos.length, 0);
|
|
81
|
+
assert.strictEqual(errores.length, 1);
|
|
82
|
+
assert.strictEqual(errores[0][1].status, undefined);
|
|
83
|
+
assert.strictEqual(errores[0][1].message, 'getaddrinfo ENOTFOUND payroll');
|
|
84
|
+
}
|
|
85
|
+
));
|
|
86
|
+
|
|
87
|
+
test('after: el log no lleva data ni apikey', conTriggers(
|
|
88
|
+
[{ endpoint: 'http://payroll-interno/x' }],
|
|
89
|
+
async ({ errores }) => {
|
|
90
|
+
axios.post = () => Promise.reject(errorHttp(500));
|
|
91
|
+
await trigger.executeTriggers(ctx, 'persona', 'after', 'update', data);
|
|
92
|
+
await esperar();
|
|
93
|
+
const texto = JSON.stringify(errores);
|
|
94
|
+
assert.ok(!texto.includes('secreta'));
|
|
95
|
+
assert.ok(!texto.includes('1234'));
|
|
96
|
+
assert.ok(!texto.includes('Ana'));
|
|
97
|
+
}
|
|
98
|
+
));
|
|
99
|
+
|
|
100
|
+
test('after: si el endpoint responde bien no se registra nada', conTriggers(
|
|
101
|
+
[{ endpoint: 'http://payroll-interno/x' }],
|
|
102
|
+
async ({ errores }) => {
|
|
103
|
+
axios.post = () => Promise.resolve({ status: 200 });
|
|
104
|
+
await trigger.executeTriggers(ctx, 'persona', 'after', 'update', data);
|
|
105
|
+
await esperar();
|
|
106
|
+
assert.strictEqual(errores.length, 0);
|
|
107
|
+
}
|
|
108
|
+
));
|
|
109
|
+
|
|
110
|
+
test('after: no espera al endpoint (la respuesta sigue sin bloquearse)', conTriggers(
|
|
111
|
+
[{ endpoint: 'http://payroll-interno/x' }],
|
|
112
|
+
async () => {
|
|
113
|
+
let resolver;
|
|
114
|
+
axios.post = () => new Promise((resolve) => { resolver = resolve; });
|
|
115
|
+
const resultado = await trigger.executeTriggers(ctx, 'persona', 'after', 'update', data);
|
|
116
|
+
assert.strictEqual(resultado, true);
|
|
117
|
+
resolver({ status: 200 });
|
|
118
|
+
}
|
|
119
|
+
));
|
|
120
|
+
|
|
121
|
+
test('after: envía data, wp y apikey al endpoint como hoy', conTriggers(
|
|
122
|
+
[{ endpoint: '/estudios/triggernivelestudios' }],
|
|
123
|
+
async () => {
|
|
124
|
+
let llamada;
|
|
125
|
+
axios.post = (...args) => { llamada = args; return Promise.resolve({}); };
|
|
126
|
+
await trigger.executeTriggers(ctx, 'estudios', 'after', 'update', data);
|
|
127
|
+
assert.deepStrictEqual(llamada, [
|
|
128
|
+
'http://persona/estudios/triggernivelestudios',
|
|
129
|
+
{ data },
|
|
130
|
+
{ headers: { wp: 'demo', apikey: 'secreta' } }
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
));
|
|
134
|
+
|
|
135
|
+
test('before: un endpoint que falla sigue haciendo fallar la operación', conTriggers(
|
|
136
|
+
[{ endpoint: 'http://payroll-interno/x' }],
|
|
137
|
+
async ({ errores }) => {
|
|
138
|
+
axios.post = () => Promise.reject(errorHttp(401));
|
|
139
|
+
await assert.rejects(
|
|
140
|
+
trigger.executeTriggers(ctx, 'persona', 'before', 'update', data),
|
|
141
|
+
/status code 401/
|
|
142
|
+
);
|
|
143
|
+
assert.strictEqual(errores.length, 0);
|
|
144
|
+
}
|
|
145
|
+
));
|
|
146
|
+
|
|
147
|
+
nodeTest('lib/trigger.js executeTriggers', async (t) => {
|
|
148
|
+
for (const [nombre, fn] of casos) {
|
|
149
|
+
await t.test(nombre, fn);
|
|
150
|
+
}
|
|
151
|
+
});
|
package/.vscode-upload.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
[{
|
|
2
|
-
"name":"",
|
|
3
|
-
"host": "",
|
|
4
|
-
"port": 22,
|
|
5
|
-
"username": "",
|
|
6
|
-
"password": "",
|
|
7
|
-
"remotePath": "",
|
|
8
|
-
"localPath": "",
|
|
9
|
-
"disable": false,
|
|
10
|
-
"private_key": "~/.ssh/id_rsa"
|
|
11
|
-
},{
|
|
12
|
-
"name":"",
|
|
13
|
-
"host": "",
|
|
14
|
-
"port": 22,
|
|
15
|
-
"username": "",
|
|
16
|
-
"password": "",
|
|
17
|
-
"remotePath": "",
|
|
18
|
-
"localPath": "",
|
|
19
|
-
"disable": false,
|
|
20
|
-
"private_key": "~/.ssh/id_rsa"
|
|
21
|
-
}]
|