apacuana-sdk-core 0.1.0 → 0.3.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 +175 -55
- package/coverage/clover.xml +249 -101
- package/coverage/coverage-final.json +9 -6
- package/coverage/lcov-report/index.html +45 -30
- package/coverage/lcov-report/src/api/certs.js.html +117 -81
- package/coverage/lcov-report/src/api/index.html +40 -25
- package/coverage/lcov-report/src/api/revocations.js.html +1 -1
- package/coverage/lcov-report/src/api/signatures.js.html +546 -72
- package/coverage/lcov-report/src/api/users.js.html +25 -61
- 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 +290 -116
- package/coverage/lcov-report/src/utils/httpClient.js.html +646 -0
- package/coverage/lcov-report/src/utils/index.html +30 -15
- package/coverage/lcov.info +515 -181
- package/dist/index.js +323 -189
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +323 -189
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api/certs.js +49 -37
- package/src/api/signatures.js +213 -49
- package/src/api/users.js +17 -29
- package/src/errors/index.js +1 -0
- package/src/index.js +4 -1
- package/src/utils/helpers.js +156 -98
- package/tests/api/certs.test.js +111 -5
- package/tests/api/signatures.test.js +152 -4
- package/tests/api/users.test.js +11 -10
- 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,183 @@ const getCertificateStatus = (userData, certificateInDevice) => {
|
|
|
41
42
|
return STATUS_CERTIFICATE.revoque;
|
|
42
43
|
};
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
const crypto = getCrypto();
|
|
50
|
-
if (!crypto) {
|
|
51
|
-
throw new Error("API criptográfica no disponible a través de pkijs.");
|
|
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
|
+
};
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
+
};
|
|
67
|
+
|
|
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"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
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
|
+
}
|
|
93
|
+
|
|
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"
|
|
72
99
|
);
|
|
73
100
|
}
|
|
74
|
-
};
|
|
75
101
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
+
}
|
|
79
112
|
|
|
80
|
-
|
|
81
|
-
if (!
|
|
82
|
-
throw new
|
|
83
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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"
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (typeof csr !== "string" || csr.trim() === "") {
|
|
158
|
+
throw new ApacuanaAPIError(
|
|
159
|
+
"El CSR debe ser una cadena de texto válida y no puede estar vacía.",
|
|
160
|
+
400,
|
|
161
|
+
"INVALID_PARAMETER_FORMAT"
|
|
122
162
|
);
|
|
163
|
+
}
|
|
123
164
|
|
|
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);
|
|
165
|
+
if (!base64Regex.test(csr)) {
|
|
139
166
|
throw new ApacuanaAPIError(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
167
|
+
"El CSR debe estar codificado en formato base64 válido.",
|
|
168
|
+
400,
|
|
169
|
+
"INVALID_PARAMETER_FORMAT"
|
|
143
170
|
);
|
|
144
171
|
}
|
|
145
172
|
};
|
|
146
173
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
+
}
|
|
151
197
|
};
|
|
152
198
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
199
|
+
const validateGetDigestData = (signData) => {
|
|
200
|
+
if (
|
|
201
|
+
!signData ||
|
|
202
|
+
!signData.cert ||
|
|
203
|
+
typeof signData.cert !== "string" ||
|
|
204
|
+
!signData.signatureId ||
|
|
205
|
+
typeof signData.signatureId !== "string"
|
|
206
|
+
) {
|
|
207
|
+
throw new ApacuanaAPIError(
|
|
208
|
+
"Los parámetros 'cert' y 'signatureId' son requeridos y deben ser cadenas de texto.",
|
|
209
|
+
400,
|
|
210
|
+
"INVALID_PARAMETER"
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
159
214
|
|
|
160
|
-
export
|
|
215
|
+
export default {
|
|
161
216
|
getCertificateStatus,
|
|
162
|
-
generateKeyPair,
|
|
163
|
-
generateCSR,
|
|
164
217
|
exportPrivateKey,
|
|
165
218
|
arrayBufferToBase64,
|
|
219
|
+
encryptedCsr,
|
|
220
|
+
validateOnBoardingSignerData,
|
|
221
|
+
validateCsr,
|
|
222
|
+
validateGetDocsData,
|
|
223
|
+
validateGetDigestData,
|
|
166
224
|
};
|
package/tests/api/certs.test.js
CHANGED
|
@@ -1,6 +1,112 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { getConfig } from "../../src/config/index";
|
|
2
|
+
import { httpRequest } from "../../src/utils/httpClient";
|
|
3
|
+
import helpers from "../../src/utils/helpers";
|
|
4
|
+
import { ApacuanaAPIError } from "../../src/errors";
|
|
5
|
+
import { generateCert, getCertStatus } from "../../src/api/certs";
|
|
6
|
+
import { INTEGRATION_TYPE } from "../../src/utils/constant";
|
|
7
|
+
|
|
8
|
+
jest.mock("../../src/config/index");
|
|
9
|
+
jest.mock("../../src/utils/httpClient");
|
|
10
|
+
jest.mock("../../src/utils/helpers");
|
|
11
|
+
|
|
12
|
+
describe("Certificate API - certs.js", () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
5
15
|
});
|
|
6
|
-
|
|
16
|
+
|
|
17
|
+
describe("generateCert", () => {
|
|
18
|
+
it("should generate a certificate for ONBOARDING integration", async () => {
|
|
19
|
+
getConfig.mockReturnValue({
|
|
20
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
21
|
+
});
|
|
22
|
+
helpers.encryptedCsr.mockReturnValue("encrypted-csr");
|
|
23
|
+
httpRequest.mockResolvedValue({ cert: "new-cert", success: true });
|
|
24
|
+
|
|
25
|
+
const result = await generateCert("some-csr");
|
|
26
|
+
|
|
27
|
+
expect(helpers.validateCsr).toHaveBeenCalledWith("some-csr");
|
|
28
|
+
expect(helpers.encryptedCsr).toHaveBeenCalledWith("some-csr");
|
|
29
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
30
|
+
"services/api/register/certificate",
|
|
31
|
+
"encrypted-csr",
|
|
32
|
+
"POST"
|
|
33
|
+
);
|
|
34
|
+
expect(result).toEqual({ cert: "new-cert", success: true });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should throw an error if API response does not contain a certificate", async () => {
|
|
38
|
+
getConfig.mockReturnValue({
|
|
39
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
40
|
+
});
|
|
41
|
+
helpers.encryptedCsr.mockReturnValue("encrypted-csr");
|
|
42
|
+
httpRequest.mockResolvedValue({ success: false });
|
|
43
|
+
|
|
44
|
+
await expect(generateCert("some-csr")).rejects.toThrow(
|
|
45
|
+
new ApacuanaAPIError(
|
|
46
|
+
"The API response does not contain the certificate.",
|
|
47
|
+
undefined,
|
|
48
|
+
"INVALID_API_RESPONSE"
|
|
49
|
+
)
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("should throw an error for ONPREMISE", async () => {
|
|
54
|
+
getConfig.mockReturnValue({
|
|
55
|
+
integrationType: INTEGRATION_TYPE.ONPREMISE,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
await expect(generateCert("some-csr")).rejects.toThrow(
|
|
59
|
+
new ApacuanaAPIError(
|
|
60
|
+
"Certificate generation is not supported for integration type: ONPREMISE",
|
|
61
|
+
501,
|
|
62
|
+
"NOT_IMPLEMENTED"
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should throw an error if integration type is not supported", async () => {
|
|
68
|
+
getConfig.mockReturnValue({ integrationType: "INVALID_TYPE" });
|
|
69
|
+
await expect(generateCert("some-csr")).rejects.toThrow(
|
|
70
|
+
"Unsupported integration type: INVALID_TYPE"
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should re-throw ApacuanaAPIError if httpRequest fails", async () => {
|
|
75
|
+
getConfig.mockReturnValue({
|
|
76
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
77
|
+
});
|
|
78
|
+
const apiError = new ApacuanaAPIError("API Error", 500, "API_ERROR");
|
|
79
|
+
httpRequest.mockRejectedValue(apiError);
|
|
80
|
+
|
|
81
|
+
await expect(generateCert("some-csr")).rejects.toThrow(apiError);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should throw a generic error if something else fails", async () => {
|
|
85
|
+
getConfig.mockReturnValue({
|
|
86
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
87
|
+
});
|
|
88
|
+
const genericError = new Error("Something failed");
|
|
89
|
+
httpRequest.mockRejectedValue(genericError);
|
|
90
|
+
|
|
91
|
+
await expect(generateCert("some-csr")).rejects.toThrow(
|
|
92
|
+
"Certificate generation failed: Something failed"
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("getCertStatus", () => {
|
|
98
|
+
it("should return the certificate status from helpers", () => {
|
|
99
|
+
const mockUserData = { certStatus: "VALID" };
|
|
100
|
+
getConfig.mockReturnValue({ userData: mockUserData });
|
|
101
|
+
helpers.getCertificateStatus.mockReturnValue("VALID");
|
|
102
|
+
|
|
103
|
+
const result = getCertStatus(true);
|
|
104
|
+
|
|
105
|
+
expect(helpers.getCertificateStatus).toHaveBeenCalledWith(
|
|
106
|
+
mockUserData,
|
|
107
|
+
true
|
|
108
|
+
);
|
|
109
|
+
expect(result).toEqual({ status: "VALID", success: true });
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -1,6 +1,154 @@
|
|
|
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, getDigest, signDocument } 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
|
+
validateGetDigestData: jest.fn(),
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe("API - Signatures", () => {
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
jest.resetAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("addSigner", () => {
|
|
25
|
+
it("should call addSignerOnBoarding for ONBOARDING integration", async () => {
|
|
26
|
+
getConfig.mockReturnValue({ integrationType: INTEGRATION_TYPE.ONBOARDING });
|
|
27
|
+
const signerData = { typedoc: "V", doc: "12345678" };
|
|
28
|
+
httpRequest.mockResolvedValue({ success: true });
|
|
29
|
+
|
|
30
|
+
await addSigner(signerData);
|
|
31
|
+
|
|
32
|
+
expect(helpers.validateOnBoardingSignerData).toHaveBeenCalledWith(
|
|
33
|
+
signerData
|
|
34
|
+
);
|
|
35
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
36
|
+
"services/api/documents/signing",
|
|
37
|
+
signerData,
|
|
38
|
+
"POST"
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("should throw not implemented for ONPREMISE integration", async () => {
|
|
43
|
+
getConfig.mockReturnValue({
|
|
44
|
+
integrationType: INTEGRATION_TYPE.ONPREMISE,
|
|
45
|
+
});
|
|
46
|
+
const signerData = { name: "John Doe" };
|
|
47
|
+
|
|
48
|
+
await expect(addSigner(signerData)).rejects.toThrow(
|
|
49
|
+
new ApacuanaAPIError(
|
|
50
|
+
"Adding signers is not supported for integration type: ONPREMISE",
|
|
51
|
+
501,
|
|
52
|
+
"NOT_IMPLEMENTED"
|
|
53
|
+
)
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should throw an error for unsupported integration type", async () => {
|
|
58
|
+
getConfig.mockReturnValue({ integrationType: "UNSUPPORTED" });
|
|
59
|
+
const signerData = { name: "test" };
|
|
60
|
+
await expect(addSigner(signerData)).rejects.toThrow(
|
|
61
|
+
new ApacuanaAPIError(
|
|
62
|
+
"Unsupported integration type: UNSUPPORTED",
|
|
63
|
+
400,
|
|
64
|
+
"UNSUPPORTED_INTEGRATION_TYPE"
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("should throw an error if signerData is invalid", async () => {
|
|
70
|
+
await expect(addSigner(null)).rejects.toThrow(
|
|
71
|
+
new ApacuanaAPIError(
|
|
72
|
+
"Signer data (signerData) is required and must be a non-empty object.",
|
|
73
|
+
400,
|
|
74
|
+
"INVALID_PARAMETER"
|
|
75
|
+
)
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("getDocs", () => {
|
|
81
|
+
const paginationData = { page: 1, size: 10 };
|
|
82
|
+
|
|
83
|
+
it("should call httpRequest with correct URL when status is provided", async () => {
|
|
84
|
+
getConfig.mockReturnValue({
|
|
85
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
86
|
+
customerId: "test-customer",
|
|
87
|
+
});
|
|
88
|
+
const dataWithStatus = { ...paginationData, status: 1 };
|
|
89
|
+
httpRequest.mockResolvedValue({ docs: [] });
|
|
90
|
+
|
|
91
|
+
await getDocs(dataWithStatus);
|
|
92
|
+
|
|
93
|
+
expect(helpers.validateGetDocsData).toHaveBeenCalledWith(dataWithStatus);
|
|
94
|
+
expect(httpRequest).toHaveBeenCalledWith(
|
|
95
|
+
"services/api/documents/listcustomer?page=1&customerid=test-customer&size=10&status=1",
|
|
96
|
+
{},
|
|
97
|
+
"GET"
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("should throw an error for ONPREMISE integration", async () => {
|
|
102
|
+
getConfig.mockReturnValue({
|
|
103
|
+
integrationType: INTEGRATION_TYPE.ONPREMISE,
|
|
104
|
+
customerId: "test-customer",
|
|
105
|
+
});
|
|
106
|
+
httpRequest.mockResolvedValue({ docs: [] });
|
|
107
|
+
|
|
108
|
+
await expect(getDocs(paginationData)).rejects.toThrow(
|
|
109
|
+
"Document retrieval is not supported for integration type: ONBOARDING"
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should throw an error if page or size are missing", async () => {
|
|
114
|
+
const validationError = new ApacuanaAPIError(
|
|
115
|
+
"Parameters 'page' and 'size' are required.",
|
|
116
|
+
400,
|
|
117
|
+
"INVALID_PARAMETER"
|
|
118
|
+
);
|
|
119
|
+
helpers.validateGetDocsData.mockImplementation(() => {
|
|
120
|
+
throw validationError;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
await expect(getDocs({})).rejects.toThrow(validationError);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("should throw an error if status is invalid", async () => {
|
|
127
|
+
const validationError = new ApacuanaAPIError(
|
|
128
|
+
"Parameter 'status' is not valid.",
|
|
129
|
+
400,
|
|
130
|
+
"INVALID_PARAMETER"
|
|
131
|
+
);
|
|
132
|
+
helpers.validateGetDocsData.mockImplementationOnce(() => {
|
|
133
|
+
throw validationError;
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
await expect(getDocs({ ...paginationData, status: 99 })).rejects.toThrow(
|
|
137
|
+
validationError
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should throw an error for non-on-premise integrations when customerId is missing", async () => {
|
|
142
|
+
getConfig.mockReturnValue({
|
|
143
|
+
integrationType: INTEGRATION_TYPE.ONBOARDING,
|
|
144
|
+
});
|
|
145
|
+
await expect(getDocs(paginationData)).rejects.toThrow(
|
|
146
|
+
new ApacuanaAPIError(
|
|
147
|
+
"'customerId' is not configured. Please configure the SDK.",
|
|
148
|
+
400,
|
|
149
|
+
"CONFIGURATION_ERROR"
|
|
150
|
+
)
|
|
151
|
+
);
|
|
152
|
+
});
|
|
5
153
|
});
|
|
6
154
|
});
|
package/tests/api/users.test.js
CHANGED
|
@@ -57,6 +57,7 @@ describe("getCustomer", () => {
|
|
|
57
57
|
);
|
|
58
58
|
|
|
59
59
|
expect(result).toEqual({
|
|
60
|
+
success: true,
|
|
60
61
|
token: "mock-session-id-12345",
|
|
61
62
|
userData: {
|
|
62
63
|
userId: "mock-user",
|
|
@@ -66,34 +67,34 @@ describe("getCustomer", () => {
|
|
|
66
67
|
});
|
|
67
68
|
});
|
|
68
69
|
|
|
69
|
-
test("
|
|
70
|
+
test("should throw an error if configuration is incomplete", async () => {
|
|
70
71
|
getConfig.mockReturnValue({
|
|
71
72
|
verificationId: "mock-verify-id",
|
|
72
73
|
customerId: null,
|
|
73
74
|
});
|
|
74
75
|
|
|
75
76
|
await expect(getCustomer()).rejects.toThrow(
|
|
76
|
-
"
|
|
77
|
+
"Both 'verificationId' and 'customerId' must be configured."
|
|
77
78
|
);
|
|
78
79
|
expect(httpRequest).not.toHaveBeenCalled();
|
|
79
80
|
});
|
|
80
81
|
|
|
81
|
-
test("
|
|
82
|
+
test("should throw ApacuanaAPIError if backend does not return sessionid or entry", async () => {
|
|
82
83
|
httpRequest.mockResolvedValueOnce({
|
|
83
84
|
success: true,
|
|
84
|
-
message: "
|
|
85
|
+
message: "Backend error, token could not be generated.",
|
|
85
86
|
entry: { userId: "mock-user" },
|
|
86
87
|
});
|
|
87
88
|
|
|
88
89
|
await expect(getCustomer()).rejects.toThrow(
|
|
89
|
-
"
|
|
90
|
+
"The API response does not contain the user."
|
|
90
91
|
);
|
|
91
92
|
expect(httpRequest).toHaveBeenCalledTimes(1);
|
|
92
93
|
});
|
|
93
94
|
|
|
94
|
-
test("
|
|
95
|
+
test("should rethrow ApacuanaAPIError if httpClient request fails", async () => {
|
|
95
96
|
const apiError = new ApacuanaAPIError(
|
|
96
|
-
"
|
|
97
|
+
"Authentication error",
|
|
97
98
|
401,
|
|
98
99
|
"AUTH_ERROR"
|
|
99
100
|
);
|
|
@@ -103,12 +104,12 @@ describe("getCustomer", () => {
|
|
|
103
104
|
expect(httpRequest).toHaveBeenCalledTimes(1);
|
|
104
105
|
});
|
|
105
106
|
|
|
106
|
-
test("
|
|
107
|
-
const genericError = new Error("
|
|
107
|
+
test("should throw ApacuanaAPIError on unexpected error", async () => {
|
|
108
|
+
const genericError = new Error("Network or unknown error");
|
|
108
109
|
httpRequest.mockRejectedValueOnce(genericError);
|
|
109
110
|
|
|
110
111
|
await expect(getCustomer()).rejects.toThrow(
|
|
111
|
-
"
|
|
112
|
+
"Unexpected failure getting token: Network or unknown error"
|
|
112
113
|
);
|
|
113
114
|
expect(httpRequest).toHaveBeenCalledTimes(1);
|
|
114
115
|
});
|
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
|
-
};
|