apacuana-sdk-core 0.1.0 → 0.2.0
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/README.md +117 -46
- package/coverage/clover.xml +205 -91
- package/coverage/coverage-final.json +9 -6
- package/coverage/lcov-report/index.html +48 -33
- package/coverage/lcov-report/src/api/certs.js.html +74 -83
- package/coverage/lcov-report/src/api/index.html +36 -21
- package/coverage/lcov-report/src/api/revocations.js.html +1 -1
- package/coverage/lcov-report/src/api/signatures.js.html +528 -81
- package/coverage/lcov-report/src/api/users.js.html +5 -2
- package/coverage/lcov-report/src/config/index.html +21 -21
- package/coverage/lcov-report/src/config/index.js.html +65 -32
- package/coverage/lcov-report/src/errors/index.html +5 -5
- package/coverage/lcov-report/src/errors/index.js.html +13 -10
- package/coverage/lcov-report/src/index.html +1 -1
- package/coverage/lcov-report/src/index.js.html +13 -4
- package/coverage/lcov-report/src/utils/constant.js.html +4 -4
- package/coverage/lcov-report/src/utils/helpers.js.html +268 -145
- package/coverage/lcov-report/src/utils/httpClient.js.html +646 -0
- package/coverage/lcov-report/src/utils/index.html +32 -17
- package/coverage/lcov.info +429 -167
- package/dist/index.js +390 -150
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +390 -150
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api/certs.js +27 -30
- package/src/api/signatures.js +205 -50
- package/src/api/users.js +1 -0
- package/src/errors/index.js +1 -0
- package/src/index.js +4 -1
- package/src/utils/helpers.js +140 -99
- package/tests/api/certs.test.js +126 -4
- package/tests/api/signatures.test.js +207 -4
- package/tests/api/users.test.js +1 -0
- package/src/auth/index.js +0 -24
package/src/utils/helpers.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as asn1js from "asn1js";
|
|
1
|
+
import CryptoJS from "crypto-js";
|
|
3
2
|
import { getCrypto } from "pkijs";
|
|
4
3
|
import { STATUS_CERTIFICATE, VERIFICATION_STATUS } from "./constant";
|
|
5
4
|
import { ApacuanaAPIError } from "../errors/index";
|
|
6
5
|
|
|
6
|
+
const KEY_HEX = "dRgUkXp2s5v8y/B?";
|
|
7
|
+
|
|
7
8
|
const getCertificateStatus = (userData, certificateInDevice) => {
|
|
8
9
|
if (!userData) {
|
|
9
10
|
throw new Error("El parámetro userData es requerido.");
|
|
@@ -41,126 +42,166 @@ const getCertificateStatus = (userData, certificateInDevice) => {
|
|
|
41
42
|
return STATUS_CERTIFICATE.revoque;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
// Función para convertir ArrayBuffer a Base64
|
|
46
|
+
const arrayBufferToBase64 = (buffer) => {
|
|
47
|
+
const binary = String.fromCharCode.apply(null, new Uint8Array(buffer));
|
|
48
|
+
return window.btoa(binary);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
async function exportPrivateKey(key) {
|
|
52
|
+
const crypto = getCrypto();
|
|
53
|
+
const exported = await crypto.exportKey("pkcs8", key);
|
|
54
|
+
const exportablePrivateKey = new Uint8Array(exported);
|
|
55
|
+
return arrayBufferToBase64(exportablePrivateKey);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const encryptedCsr = (csr, encryptKey = KEY_HEX) => {
|
|
59
|
+
const body = {
|
|
60
|
+
csr: CryptoJS.AES.encrypt(csr, encryptKey, {
|
|
61
|
+
mode: CryptoJS.mode.ECB,
|
|
62
|
+
padding: CryptoJS.pad.Pkcs7,
|
|
63
|
+
}).toString(),
|
|
64
|
+
};
|
|
65
|
+
return body;
|
|
66
|
+
};
|
|
53
67
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
true,
|
|
62
|
-
["sign", "verify"]
|
|
68
|
+
const validateOnBoardingSignerData = (signerData) => {
|
|
69
|
+
if (!signerData.name || typeof signerData.name !== "string") {
|
|
70
|
+
throw new ApacuanaAPIError(
|
|
71
|
+
'El campo "name" es requerido y debe ser una cadena de texto.',
|
|
72
|
+
400,
|
|
73
|
+
"INVALID_PARAMETER_FORMAT"
|
|
63
74
|
);
|
|
75
|
+
}
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error("Error durante la generación del par de claves:", error);
|
|
77
|
+
if (!signerData.reference || typeof signerData.reference !== "string") {
|
|
68
78
|
throw new ApacuanaAPIError(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"
|
|
79
|
+
'El campo "reference" es requerido y debe ser una cadena de texto.',
|
|
80
|
+
400,
|
|
81
|
+
"INVALID_PARAMETER_FORMAT"
|
|
72
82
|
);
|
|
73
83
|
}
|
|
74
|
-
};
|
|
75
84
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
const validDocTypes = ["V", "P", "E"];
|
|
86
|
+
if (!signerData.typedoc || !validDocTypes.includes(signerData.typedoc)) {
|
|
87
|
+
throw new ApacuanaAPIError(
|
|
88
|
+
'El campo "typedoc" es requerido y debe ser uno de los siguientes valores: V, P, E.',
|
|
89
|
+
400,
|
|
90
|
+
"INVALID_PARAMETER_FORMAT"
|
|
91
|
+
);
|
|
92
|
+
}
|
|
79
93
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
if (!signerData.doc || typeof signerData.doc !== "string") {
|
|
95
|
+
throw new ApacuanaAPIError(
|
|
96
|
+
'El campo "doc" es requerido y debe ser una cadena de texto.',
|
|
97
|
+
400,
|
|
98
|
+
"INVALID_PARAMETER_FORMAT"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (
|
|
103
|
+
!Array.isArray(signerData.signature) ||
|
|
104
|
+
signerData.signature.length === 0
|
|
105
|
+
) {
|
|
106
|
+
throw new ApacuanaAPIError(
|
|
107
|
+
'El campo "signature" es requerido y debe ser un array no vacío.',
|
|
108
|
+
400,
|
|
109
|
+
"INVALID_PARAMETER_FORMAT"
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
signerData.signature.forEach((sig, index) => {
|
|
114
|
+
if (!Number.isInteger(sig.page) || sig.page < 1) {
|
|
115
|
+
throw new ApacuanaAPIError(
|
|
116
|
+
`La página de la firma ${
|
|
117
|
+
index + 1
|
|
118
|
+
} debe ser un número entero positivo.`,
|
|
119
|
+
400,
|
|
120
|
+
"INVALID_PARAMETER_FORMAT"
|
|
84
121
|
);
|
|
85
122
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
123
|
+
|
|
124
|
+
if (typeof sig.x !== "number" || sig.x < 0 || sig.x > 1) {
|
|
125
|
+
throw new ApacuanaAPIError(
|
|
126
|
+
`La coordenada X de la firma ${
|
|
127
|
+
index + 1
|
|
128
|
+
} debe ser un número entre 0 y 1.`,
|
|
129
|
+
400,
|
|
130
|
+
"INVALID_PARAMETER_FORMAT"
|
|
89
131
|
);
|
|
90
132
|
}
|
|
91
133
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// },
|
|
113
|
-
// {
|
|
114
|
-
// type: '2.5.4.7', // OID para 'Locality' (L)
|
|
115
|
-
// value: new asn1js.Utf8String({ value: userData.locality || 'N/A' }),
|
|
116
|
-
// },
|
|
117
|
-
].filter((attr) => attr.value.value); // Filtra atributos sin valor.
|
|
118
|
-
|
|
119
|
-
// 3. Asignar los atributos al Subject.
|
|
120
|
-
subjectAttributes.forEach((attr) =>
|
|
121
|
-
pkcs10.subject.typesAndValues.push(new pkijs.AttributeTypeAndValue(attr))
|
|
134
|
+
if (typeof sig.y !== "number" || sig.y < 0 || sig.y > 1) {
|
|
135
|
+
throw new ApacuanaAPIError(
|
|
136
|
+
`La coordenada Y de la firma ${
|
|
137
|
+
index + 1
|
|
138
|
+
} debe ser un número entre 0 y 1.`,
|
|
139
|
+
400,
|
|
140
|
+
"INVALID_PARAMETER_FORMAT"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const validateCsr = (csr) => {
|
|
147
|
+
const base64Regex = /^[A-Za-z0-9+/=]+$/;
|
|
148
|
+
|
|
149
|
+
if (!csr) {
|
|
150
|
+
throw new ApacuanaAPIError(
|
|
151
|
+
"La solicitud de firma de certificado (CSR) es requerida.",
|
|
152
|
+
400,
|
|
153
|
+
"INVALID_PARAMETER"
|
|
122
154
|
);
|
|
155
|
+
}
|
|
123
156
|
|
|
124
|
-
|
|
125
|
-
await pkcs10.subjectPublicKeyInfo.importKey(publicKey);
|
|
126
|
-
await pkcs10.sign(privateKey, hashAlg);
|
|
127
|
-
|
|
128
|
-
// 5. Codificar el CSR para el envío.
|
|
129
|
-
const csr = pkcs10.toSchema().toBER(false);
|
|
130
|
-
|
|
131
|
-
// 6. Retornar el CSR y el Subject.
|
|
132
|
-
return {
|
|
133
|
-
csr,
|
|
134
|
-
subject: pkcs10.subject,
|
|
135
|
-
};
|
|
136
|
-
} catch (error) {
|
|
137
|
-
// Manejo de errores centralizado.
|
|
138
|
-
console.error("Error durante la generación del CSR:", error);
|
|
157
|
+
if (typeof csr !== "string" || csr.trim() === "") {
|
|
139
158
|
throw new ApacuanaAPIError(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
159
|
+
"El CSR debe ser una cadena de texto válida y no puede estar vacía.",
|
|
160
|
+
400,
|
|
161
|
+
"INVALID_PARAMETER_FORMAT"
|
|
143
162
|
);
|
|
144
163
|
}
|
|
145
|
-
};
|
|
146
164
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
165
|
+
if (!base64Regex.test(csr)) {
|
|
166
|
+
throw new ApacuanaAPIError(
|
|
167
|
+
"El CSR debe estar codificado en formato base64 válido.",
|
|
168
|
+
400,
|
|
169
|
+
"INVALID_PARAMETER_FORMAT"
|
|
170
|
+
);
|
|
171
|
+
}
|
|
151
172
|
};
|
|
152
173
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
174
|
+
const validateGetDocsData = (data) => {
|
|
175
|
+
if (
|
|
176
|
+
!data ||
|
|
177
|
+
typeof data.page === "undefined" ||
|
|
178
|
+
typeof data.size === "undefined"
|
|
179
|
+
) {
|
|
180
|
+
throw new ApacuanaAPIError(
|
|
181
|
+
"Los parámetros 'page' y 'size' son requeridos.",
|
|
182
|
+
400,
|
|
183
|
+
"INVALID_PARAMETER"
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (
|
|
188
|
+
typeof data.status !== "undefined" &&
|
|
189
|
+
![-1, 0, 1, 2].includes(data.status)
|
|
190
|
+
) {
|
|
191
|
+
throw new ApacuanaAPIError(
|
|
192
|
+
"El parámetro 'status' no es válido. Los valores permitidos son: -1, 0, 1, 2.",
|
|
193
|
+
400,
|
|
194
|
+
"INVALID_PARAMETER"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
159
198
|
|
|
160
|
-
export
|
|
199
|
+
export default {
|
|
161
200
|
getCertificateStatus,
|
|
162
|
-
generateKeyPair,
|
|
163
|
-
generateCSR,
|
|
164
201
|
exportPrivateKey,
|
|
165
202
|
arrayBufferToBase64,
|
|
203
|
+
encryptedCsr,
|
|
204
|
+
validateOnBoardingSignerData,
|
|
205
|
+
validateCsr,
|
|
206
|
+
validateGetDocsData,
|
|
166
207
|
};
|
package/tests/api/certs.test.js
CHANGED
|
@@ -1,6 +1,128 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { generateCert, getCertStatus } from "../../src/api/certs";
|
|
2
|
+
import { getConfig } from "../../src/config";
|
|
3
|
+
import { httpRequest } from "../../src/utils/httpClient";
|
|
4
|
+
import { ApacuanaAPIError } from "../../src/errors";
|
|
5
|
+
import {
|
|
6
|
+
INTEGRATION_TYPE,
|
|
7
|
+
STATUS_CERTIFICATE,
|
|
8
|
+
} from "../../src/utils/constant";
|
|
9
|
+
|
|
10
|
+
jest.mock("../../src/utils/httpClient");
|
|
11
|
+
jest.mock("../../src/config");
|
|
12
|
+
|
|
13
|
+
describe("API de Certificados - certs.js", () => {
|
|
14
|
+
const validBase64Csr = "dGVzdA=="; // "test" in base64
|
|
15
|
+
|
|
16
|
+
afterEach(() => {
|
|
17
|
+
jest.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("generateCert", () => {
|
|
21
|
+
it("debería generar un certificado con éxito para ONBOARDING", async () => {
|
|
22
|
+
const mockResponse = { cert: "new-cert", success: true };
|
|
23
|
+
httpRequest.mockResolvedValue(mockResponse);
|
|
24
|
+
getConfig.mockReturnValue({
|
|
25
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const result = await generateCert(validBase64Csr);
|
|
29
|
+
|
|
30
|
+
expect(result).toEqual(mockResponse);
|
|
31
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
32
|
+
"services/api/register/certificate",
|
|
33
|
+
expect.any(Object),
|
|
34
|
+
"POST"
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("debería lanzar un error si el CSR no se proporciona", async () => {
|
|
39
|
+
getConfig.mockReturnValue({
|
|
40
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
41
|
+
});
|
|
42
|
+
await expect(generateCert()).rejects.toThrow(
|
|
43
|
+
new ApacuanaAPIError(
|
|
44
|
+
"La solicitud de firma de certificado (CSR) es requerida.",
|
|
45
|
+
400,
|
|
46
|
+
"CSR_REQUIRED"
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("debería lanzar un error si el CSR no es un string", async () => {
|
|
52
|
+
getConfig.mockReturnValue({
|
|
53
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
54
|
+
});
|
|
55
|
+
await expect(generateCert(12345)).rejects.toThrow(
|
|
56
|
+
new ApacuanaAPIError(
|
|
57
|
+
"El CSR debe ser una cadena de texto válida y no puede estar vacía.",
|
|
58
|
+
400,
|
|
59
|
+
"INVALID_CSR_FORMAT"
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("debería lanzar un error si el CSR no está en formato Base64", async () => {
|
|
65
|
+
getConfig.mockReturnValue({
|
|
66
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
67
|
+
});
|
|
68
|
+
await expect(generateCert("csr-invalido")).rejects.toThrow(
|
|
69
|
+
new ApacuanaAPIError(
|
|
70
|
+
"El CSR debe estar codificado en formato base64 válido.",
|
|
71
|
+
400,
|
|
72
|
+
"INVALID_CSR_ENCODING"
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("debería manejar la lógica para ONPREMISE", async () => {
|
|
78
|
+
getConfig.mockReturnValue({ integrationType: INTEGRATION_TYPE.ONPREMISE });
|
|
79
|
+
const consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {});
|
|
80
|
+
await generateCert(validBase64Csr);
|
|
81
|
+
expect(consoleSpy).toHaveBeenCalledWith("-> Lógica para On-premise");
|
|
82
|
+
consoleSpy.mockRestore();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("debería lanzar un error para tipos de integración no soportados", async () => {
|
|
86
|
+
getConfig.mockReturnValue({ integrationType: "UNSUPPORTED_TYPE" });
|
|
87
|
+
await expect(generateCert(validBase64Csr)).rejects.toThrow(
|
|
88
|
+
new ApacuanaAPIError(
|
|
89
|
+
"Tipo de integración no soportado: UNSUPPORTED_TYPE",
|
|
90
|
+
400,
|
|
91
|
+
"UNSUPPORTED_INTEGRATION_TYPE"
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("debería lanzar un ApacuanaAPIError si la respuesta de la API no contiene el certificado", async () => {
|
|
97
|
+
httpRequest.mockResolvedValue({ success: true }); // No 'cert' property
|
|
98
|
+
getConfig.mockReturnValue({
|
|
99
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await expect(generateCert(validBase64Csr)).rejects.toThrow(
|
|
103
|
+
new ApacuanaAPIError(
|
|
104
|
+
"La respuesta de la API no contiene el certificado.",
|
|
105
|
+
undefined,
|
|
106
|
+
"INVALID_API_RESPONSE"
|
|
107
|
+
)
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("getCertStatus", () => {
|
|
113
|
+
it("debería devolver el estado del certificado correctamente", () => {
|
|
114
|
+
const mockUserData = {
|
|
115
|
+
cert: "some-cert-data",
|
|
116
|
+
verificationstatus: { id: 1 }, // VERIFICATION_STATUS.APPROVED
|
|
117
|
+
certificationId: "some-cert-id",
|
|
118
|
+
approvedUser: true,
|
|
119
|
+
};
|
|
120
|
+
getConfig.mockReturnValue({ userData: mockUserData });
|
|
121
|
+
const result = getCertStatus(true);
|
|
122
|
+
expect(result).toEqual({
|
|
123
|
+
status: STATUS_CERTIFICATE.current,
|
|
124
|
+
success: true,
|
|
125
|
+
});
|
|
126
|
+
});
|
|
5
127
|
});
|
|
6
128
|
});
|
|
@@ -1,6 +1,209 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getConfig } from "../../src/config";
|
|
2
|
+
import { httpRequest } from "../../src/utils/httpClient";
|
|
3
|
+
import { ApacuanaAPIError } from "../../src/errors";
|
|
4
|
+
import { addSigner, getDocs } from "../../src/api/signatures";
|
|
5
|
+
import helpers from "../../src/utils/helpers";
|
|
6
|
+
import { INTEGRATION_TYPE } from "../../src/utils/constant";
|
|
7
|
+
|
|
8
|
+
jest.mock("../../src/config");
|
|
9
|
+
jest.mock("../../src/utils/httpClient");
|
|
10
|
+
jest.mock("../../src/utils/helpers", () => ({
|
|
11
|
+
validateOnBoardingSignerData: jest.fn(),
|
|
12
|
+
validateSignOnBoardingData: jest.fn(),
|
|
13
|
+
validateSignOnPremiseData: jest.fn(),
|
|
14
|
+
signDigest: jest.fn(),
|
|
15
|
+
validateGetDocsData: jest.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
describe("API - Signatures", () => {
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
jest.resetAllMocks();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("addSigner", () => {
|
|
24
|
+
it("should call addSignerOnBoarding for ONBOARDING integration", async () => {
|
|
25
|
+
getConfig.mockReturnValue({ integrationType: INTEGRATION_TYPE.ONBOARDING });
|
|
26
|
+
const signerData = { typedoc: "V", doc: "12345678" };
|
|
27
|
+
httpRequest.mockResolvedValue({ success: true });
|
|
28
|
+
|
|
29
|
+
await addSigner(signerData);
|
|
30
|
+
|
|
31
|
+
expect(helpers.validateOnBoardingSignerData).toHaveBeenCalledWith(
|
|
32
|
+
signerData
|
|
33
|
+
);
|
|
34
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
35
|
+
"services/api/documents/signing",
|
|
36
|
+
signerData,
|
|
37
|
+
"POST"
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should call addSignerOnPremise for ONPREMISE integration", async () => {
|
|
42
|
+
getConfig.mockReturnValue({
|
|
43
|
+
integrationType: INTEGRATION_TYPE.ONPREMISE,
|
|
44
|
+
});
|
|
45
|
+
const signerData = { name: "John Doe" };
|
|
46
|
+
|
|
47
|
+
const result = await addSigner(signerData);
|
|
48
|
+
|
|
49
|
+
expect(result).toEqual({
|
|
50
|
+
signerId: "on-premise-signer-id",
|
|
51
|
+
status: "added",
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("should throw an error for unsupported integration type", async () => {
|
|
56
|
+
getConfig.mockReturnValue({ integrationType: "UNSUPPORTED" });
|
|
57
|
+
const signerData = { name: "test" };
|
|
58
|
+
await expect(addSigner(signerData)).rejects.toThrow(
|
|
59
|
+
new ApacuanaAPIError(
|
|
60
|
+
"Tipo de integración no soportado: UNSUPPORTED",
|
|
61
|
+
400,
|
|
62
|
+
"UNSUPPORTED_INTEGRATION_TYPE"
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should throw an error if signerData is invalid", async () => {
|
|
68
|
+
await expect(addSigner(null)).rejects.toThrow(
|
|
69
|
+
new ApacuanaAPIError(
|
|
70
|
+
"Los datos del firmante (signerData) son requeridos y deben ser un objeto no vacío.",
|
|
71
|
+
400,
|
|
72
|
+
"INVALID_PARAMETER"
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/*
|
|
79
|
+
describe("signDocument", () => {
|
|
80
|
+
const onBoardingData = {
|
|
81
|
+
documentData: { id: "doc1" },
|
|
82
|
+
signatureData: { id: "sig1" },
|
|
83
|
+
};
|
|
84
|
+
const onPremiseData = {
|
|
85
|
+
signature: { id: "sig1", positions: [] },
|
|
86
|
+
cert: "my-cert",
|
|
87
|
+
privateKey: "my-key",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
it("should call signDocumentOnBoarding for ONBOARDING integration", async () => {
|
|
91
|
+
getConfig.mockReturnValue({ integrationType: INTEGRATION_TYPE.ONBOARDING });
|
|
92
|
+
const mockResponse = {
|
|
93
|
+
success: true,
|
|
94
|
+
id: "signed-doc-id",
|
|
95
|
+
message: "Firmado",
|
|
96
|
+
};
|
|
97
|
+
httpRequest.mockResolvedValue(mockResponse);
|
|
98
|
+
|
|
99
|
+
const result = await signDocument(onBoardingData);
|
|
100
|
+
|
|
101
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
102
|
+
"/docs/sign",
|
|
103
|
+
onBoardingData,
|
|
104
|
+
"POST"
|
|
105
|
+
);
|
|
106
|
+
expect(result).toEqual(mockResponse);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("should throw TypeError for ONPREMISE integration due to implementation issue", async () => {
|
|
110
|
+
getConfig.mockReturnValue({
|
|
111
|
+
integrationType: INTEGRATION_TYPE.ONPREMISE,
|
|
112
|
+
});
|
|
113
|
+
httpRequest
|
|
114
|
+
.mockResolvedValueOnce({ data: { digest: "test-digest" } })
|
|
115
|
+
.mockResolvedValueOnce({ data: "signed-response" });
|
|
116
|
+
helpers.signDigest.mockResolvedValue("signed-digest");
|
|
117
|
+
|
|
118
|
+
await expect(signDocument(onPremiseData)).rejects.toThrow(
|
|
119
|
+
"Fallo en la firma del documento (on-premise): Cannot read properties of undefined (reading 'map')"
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should throw an error if data is invalid", async () => {
|
|
124
|
+
await expect(signDocument({})).rejects.toThrow(
|
|
125
|
+
new ApacuanaAPIError(
|
|
126
|
+
"El parámetro 'data' es requerido y debe ser un objeto no vacío.",
|
|
127
|
+
400,
|
|
128
|
+
"INVALID_PARAMETER"
|
|
129
|
+
)
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
describe("getDocs", () => {
|
|
136
|
+
const paginationData = { page: 1, size: 10 };
|
|
137
|
+
|
|
138
|
+
it("should call httpRequest with correct URL when status is provided", async () => {
|
|
139
|
+
getConfig.mockReturnValue({
|
|
140
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
141
|
+
customerId: "test-customer",
|
|
142
|
+
});
|
|
143
|
+
const dataWithStatus = { ...paginationData, status: 1 };
|
|
144
|
+
httpRequest.mockResolvedValue({ docs: [] });
|
|
145
|
+
|
|
146
|
+
await getDocs(dataWithStatus);
|
|
147
|
+
|
|
148
|
+
expect(helpers.validateGetDocsData).toHaveBeenCalledWith(dataWithStatus);
|
|
149
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
150
|
+
"services/api/documents/listcustomer?page=1&customerid=test-customer&size=10&status=1",
|
|
151
|
+
{},
|
|
152
|
+
"GET"
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should throw an error for ONPREMISE integration due to implementation issue", async () => {
|
|
157
|
+
getConfig.mockReturnValue({
|
|
158
|
+
integrationType: INTEGRATION_TYPE.ONPREMISE,
|
|
159
|
+
customerId: "test-customer",
|
|
160
|
+
});
|
|
161
|
+
httpRequest.mockResolvedValue({ docs: [] });
|
|
162
|
+
|
|
163
|
+
await expect(getDocs(paginationData)).rejects.toThrow(
|
|
164
|
+
"La obtención de documentos no está soportada para el tipo de integración: ONBOARDING"
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("should throw an error if page or size are missing", async () => {
|
|
169
|
+
const validationError = new ApacuanaAPIError(
|
|
170
|
+
"Los parámetros 'page' y 'size' son requeridos.",
|
|
171
|
+
400,
|
|
172
|
+
"INVALID_PARAMETER"
|
|
173
|
+
);
|
|
174
|
+
helpers.validateGetDocsData.mockImplementation(() => {
|
|
175
|
+
throw validationError;
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
await expect(getDocs({})).rejects.toThrow(validationError);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("should throw an error if status is invalid", async () => {
|
|
182
|
+
const validationError = new ApacuanaAPIError(
|
|
183
|
+
"El parámetro 'status' no es válido.",
|
|
184
|
+
400,
|
|
185
|
+
"INVALID_PARAMETER"
|
|
186
|
+
);
|
|
187
|
+
helpers.validateGetDocsData.mockImplementationOnce(() => {
|
|
188
|
+
throw validationError;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await expect(getDocs({ ...paginationData, status: 99 })).rejects.toThrow(
|
|
192
|
+
validationError
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should throw an error for non-on-premise integrations", async () => {
|
|
197
|
+
getConfig.mockReturnValue({
|
|
198
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
199
|
+
});
|
|
200
|
+
await expect(getDocs(paginationData)).rejects.toThrow(
|
|
201
|
+
new ApacuanaAPIError(
|
|
202
|
+
"El 'customerId' no está configurado. Por favor, configure el SDK.",
|
|
203
|
+
400,
|
|
204
|
+
"CONFIGURATION_ERROR"
|
|
205
|
+
)
|
|
206
|
+
);
|
|
207
|
+
});
|
|
5
208
|
});
|
|
6
209
|
});
|
package/tests/api/users.test.js
CHANGED
package/src/auth/index.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
// src/auth/index.js
|
|
2
|
-
// Si httpClient construye los headers directamente de la config,
|
|
3
|
-
// este módulo se vuelve menos crítico o puede desaparecer por ahora.
|
|
4
|
-
// Lo dejaremos por si hay lógica de auth más compleja después.
|
|
5
|
-
import { getConfig } from '../config/index';
|
|
6
|
-
|
|
7
|
-
export const getAuthHeaders = () => {
|
|
8
|
-
const { secretKey, apiKey } = getConfig();
|
|
9
|
-
if (!secretKey || !apiKey) {
|
|
10
|
-
throw new Error('Apacuana SDK: secretKey o apiKey no configuradas.');
|
|
11
|
-
}
|
|
12
|
-
return {
|
|
13
|
-
'X-Api-Key': apiKey,
|
|
14
|
-
Authorization: `Bearer ${secretKey}`,
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
export const getVerificationId = () => {
|
|
19
|
-
const { verificationId } = getConfig();
|
|
20
|
-
if (!verificationId) {
|
|
21
|
-
throw new Error('Apacuana SDK: verificationId no configurado.');
|
|
22
|
-
}
|
|
23
|
-
return verificationId;
|
|
24
|
-
};
|