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.
Files changed (35) hide show
  1. package/README.md +175 -55
  2. package/coverage/clover.xml +249 -101
  3. package/coverage/coverage-final.json +9 -6
  4. package/coverage/lcov-report/index.html +45 -30
  5. package/coverage/lcov-report/src/api/certs.js.html +117 -81
  6. package/coverage/lcov-report/src/api/index.html +40 -25
  7. package/coverage/lcov-report/src/api/revocations.js.html +1 -1
  8. package/coverage/lcov-report/src/api/signatures.js.html +546 -72
  9. package/coverage/lcov-report/src/api/users.js.html +25 -61
  10. package/coverage/lcov-report/src/config/index.html +21 -21
  11. package/coverage/lcov-report/src/config/index.js.html +65 -32
  12. package/coverage/lcov-report/src/errors/index.html +5 -5
  13. package/coverage/lcov-report/src/errors/index.js.html +13 -10
  14. package/coverage/lcov-report/src/index.html +1 -1
  15. package/coverage/lcov-report/src/index.js.html +13 -4
  16. package/coverage/lcov-report/src/utils/constant.js.html +4 -4
  17. package/coverage/lcov-report/src/utils/helpers.js.html +290 -116
  18. package/coverage/lcov-report/src/utils/httpClient.js.html +646 -0
  19. package/coverage/lcov-report/src/utils/index.html +30 -15
  20. package/coverage/lcov.info +515 -181
  21. package/dist/index.js +323 -189
  22. package/dist/index.js.map +1 -1
  23. package/dist/index.mjs +323 -189
  24. package/dist/index.mjs.map +1 -1
  25. package/package.json +1 -1
  26. package/src/api/certs.js +49 -37
  27. package/src/api/signatures.js +213 -49
  28. package/src/api/users.js +17 -29
  29. package/src/errors/index.js +1 -0
  30. package/src/index.js +4 -1
  31. package/src/utils/helpers.js +156 -98
  32. package/tests/api/certs.test.js +111 -5
  33. package/tests/api/signatures.test.js +152 -4
  34. package/tests/api/users.test.js +11 -10
  35. package/src/auth/index.js +0 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apacuana-sdk-core",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Core SDK para interacciones con las APIs de Apacuana.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
package/src/api/certs.js CHANGED
@@ -1,70 +1,82 @@
1
- import CryptoJS from "crypto-js";
2
- import { getConfig } from "../config";
3
- import { INTEGRATION_TYPE } from "../utils/constant";
4
- import { helpers } from "../utils/helpers";
5
- import { ApacuanaAPIError } from "../errors/index";
1
+ import { getConfig } from "../config/index";
6
2
  import { httpRequest } from "../utils/httpClient";
3
+ import helpers from "../utils/helpers";
4
+ import { ApacuanaAPIError } from "../errors";
5
+ import { INTEGRATION_TYPE } from "../utils/constant";
7
6
 
8
- const generateCertOnBoarding = async () => {
7
+ const generateCertOnBoarding = async (csr = undefined) => {
9
8
  try {
10
- const config = getConfig();
11
- const { userData } = config;
12
- const keyPair = await helpers.generateKeyPair();
13
- const { csrBase64 } = await helpers.generateCSR(keyPair, userData);
14
-
15
- const body = {
16
- csr: CryptoJS.AES.encrypt(csrBase64, config.secretKey, {
17
- mode: CryptoJS.mode.ECB,
18
- padding: CryptoJS.pad.Pkcs7,
19
- }).toString(),
20
- };
9
+ const encryptedCSR = helpers.encryptedCsr(csr);
21
10
 
22
11
  const response = await httpRequest(
23
12
  "services/api/register/certificate",
24
- body,
13
+ encryptedCSR,
25
14
  "POST"
26
15
  );
27
-
28
16
  const { cert } = response;
29
17
  if (!cert) {
30
18
  throw new ApacuanaAPIError(
31
- "La respuesta de la API no contiene el certificado.",
19
+ "The API response does not contain the certificate.",
32
20
  response.status,
33
21
  "INVALID_API_RESPONSE"
34
22
  );
35
23
  }
36
24
 
37
- const exportedPrivateKey = await helpers.exportPrivateKey(
38
- keyPair.privateKey
39
- );
40
-
41
- return { cert, privateKey: exportedPrivateKey };
25
+ return { cert, success: true };
42
26
  } catch (error) {
43
- // Se lanza el error capturado, que ya será de tipo ApacuanaAPIError o un Error nativo.
27
+ // The captured error is re-thrown, which will already be of type ApacuanaAPIError or a native Error.
44
28
  if (error instanceof ApacuanaAPIError) {
45
29
  throw error;
46
30
  }
47
- throw new Error(`Fallo en la generación del certificado: ${error.message}`);
31
+ throw new Error(`Certificate generation failed: ${error.message}`);
48
32
  }
49
33
  };
50
34
 
51
- export const generateCert = async () => {
52
- const { integrationType } = getConfig();
35
+ const generateCertOnPremise = async () => {
36
+ throw new ApacuanaAPIError(
37
+ "Certificate generation is not supported for integration type: ONPREMISE",
38
+ 501,
39
+ "NOT_IMPLEMENTED"
40
+ );
41
+ };
53
42
 
43
+ /**
44
+ * Generates a digital certificate.
45
+ * @param {string} [csr] - Optional Certificate Signing Request (CSR).
46
+ * @returns {Promise<{cert: string, success: boolean}>} Object with the generated certificate and a success indicator.
47
+ * @throws {ApacuanaAPIError} If the CSR is invalid, if the API response does not contain the certificate, or if the integration type is not supported.
48
+ * @throws {Error} If certificate generation fails for another reason.
49
+ */
50
+ export const generateCert = async (csr = undefined) => {
51
+ const { integrationType } = getConfig();
52
+ helpers.validateCsr(csr);
54
53
  if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
55
- await generateCertOnBoarding();
56
- } else if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
57
- // eslint-disable-next-line no-console
58
- console.log("-> Lógica para On-premise");
59
- // Aquí iría la llamada a httpRequest para el endpoint de on-premise
60
- // return httpRequest('/certs/generate-onpremise', data);
54
+ return generateCertOnBoarding(csr);
61
55
  }
56
+ if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
57
+ return generateCertOnPremise();
58
+ }
59
+
60
+ throw new ApacuanaAPIError(
61
+ `Unsupported integration type: ${integrationType}`,
62
+ 400,
63
+ "UNSUPPORTED_INTEGRATION_TYPE"
64
+ );
62
65
  };
63
66
 
64
- export const getCertStatus = () => {
67
+ /**
68
+ * Gets the user's certificate status.
69
+ * @param {boolean} [isCertificateInDevice=false] - Indicates if the certificate is already on the device.
70
+ * @returns {{status: string, success: boolean}} Object with the certificate status and a success indicator.
71
+ */
72
+ export const getCertStatus = (isCertificateInDevice = false) => {
65
73
  const config = getConfig();
66
- const status = helpers.getCertificateStatus(config.userData, false);
74
+ const status = helpers.getCertificateStatus(
75
+ config.userData,
76
+ isCertificateInDevice
77
+ );
67
78
  return {
68
79
  status,
80
+ success: true,
69
81
  };
70
82
  };
@@ -1,77 +1,241 @@
1
1
  // src/api/signatures.js
2
- import { httpRequest } from '../utils/httpClient';
3
- import { ApacuanaAPIError } from '../errors/index';
2
+ import { getConfig } from "../config/index";
3
+ import { httpRequest } from "../utils/httpClient";
4
+ import { ApacuanaAPIError } from "../errors";
5
+ import helpers from "../utils/helpers";
6
+ import { INTEGRATION_TYPE } from "../utils/constant";
7
+
8
+ const signDocumentOnBoarding = async () => {
9
+ throw new ApacuanaAPIError(
10
+ "Document signing is not supported for integration type: ONBOARDING",
11
+ 501,
12
+ "NOT_IMPLEMENTED"
13
+ );
14
+ };
15
+
16
+ const signDocumentOnPremise = async () => {
17
+ throw new ApacuanaAPIError(
18
+ "Document signing is not supported for integration type: ONPREMISE",
19
+ 501,
20
+ "NOT_IMPLEMENTED"
21
+ );
22
+ };
4
23
 
5
24
  /**
6
- * Simula la firma de un documento.
7
- * @param {object} documentData - Datos del documento a firmar.
8
- * @param {object} signatureData - Datos de la firma.
9
- * @returns {Promise<object>} Objeto con el resultado de la firma.
25
+ * Signs a PDF document with a digital certificate.
26
+ * @param {object} data - Data for the signature. For 'on-boarding', it must contain {documentData, signatureData}. For 'on-premise', it must contain {signature, cert, privateKey}.
27
+ * @returns {Promise<{digest: string}>} Object with the document's digest.
28
+ * @throws {ApacuanaAPIError} If the integration type is not supported or the function is not implemented for the integration type.
10
29
  */
11
- export const signDocument = async (documentData, signatureData) => {
12
- console.log("-> Función 'signDocument' ejecutada.");
13
- console.log('Parámetros recibidos para firmar documento:', {
14
- documentData,
15
- signatureData,
16
- });
17
-
18
- if (!documentData || !signatureData) {
19
- throw new Error(
20
- 'Datos de documento y firma son requeridos para signDocument.',
21
- );
30
+ export const signDocument = async () => {
31
+ const { integrationType } = getConfig();
32
+
33
+ if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
34
+ return signDocumentOnBoarding();
35
+ }
36
+
37
+ if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
38
+ return signDocumentOnPremise();
22
39
  }
23
40
 
41
+ throw new ApacuanaAPIError(
42
+ `Unsupported integration type: ${integrationType}`,
43
+ 400,
44
+ "UNSUPPORTED_INTEGRATION_TYPE"
45
+ );
46
+ };
47
+
48
+ const getDigestToSignOnBoarding = async (signData) => {
24
49
  try {
25
50
  const response = await httpRequest(
26
- '/docs/sign',
27
- { documentData, signatureData },
28
- 'POST',
51
+ `services/api/documents/getdigest/${signData.signatureId}`,
52
+ {
53
+ publickey: signData.cert,
54
+ },
55
+ "POST"
29
56
  );
30
- console.log(
31
- 'Respuesta del servidor simulada para firmar documento:',
32
- response,
33
- );
34
- if (!response.success) {
57
+ const digest = response.data?.digest || response.digest;
58
+ if (!digest) {
35
59
  throw new ApacuanaAPIError(
36
- response.message || 'Error desconocido al firmar documento.',
60
+ "Signature generation failed: digest not found in the response.",
61
+ 500,
62
+ "API_RESPONSE_ERROR"
37
63
  );
38
64
  }
39
- return { signedDocId: response.id, status: response.message };
65
+
66
+ return { digest, success: true };
40
67
  } catch (error) {
41
- throw new Error(`Fallo en la firma del documento: ${error.message}`);
68
+ if (error.name === "ApacuanaAPIError") {
69
+ throw error;
70
+ }
71
+ throw new ApacuanaAPIError(
72
+ `Failed to sign document (on-boarding): ${error.message}`
73
+ );
42
74
  }
43
75
  };
44
76
 
77
+ const getDigestToSignOnPremise = async () => {
78
+ throw new ApacuanaAPIError(
79
+ "Digest retrieval is not supported for integration type: ONPREMISE",
80
+ 501,
81
+ "NOT_IMPLEMENTED"
82
+ );
83
+ };
84
+
45
85
  /**
46
- * Simula la adición de un firmante a un documento.
47
- * @param {object} signerData - Datos del firmante a agregar.
48
- * @returns {Promise<object>} Objeto con el resultado de agregar el firmante.
86
+ * Gets the digest of a document to be signed.
87
+ * @param {object} signData - Data for the signature. Must contain { cert, signatureId }.
88
+ * @returns {Promise<{digest: string, success: boolean}>} Object with the document's digest.
89
+ * @throws {ApacuanaAPIError} If the input data is invalid, if the API call fails, or if the integration type is not supported.
49
90
  */
50
- export const addSigner = async (signerData) => {
51
- console.log("-> Función 'addSigner' ejecutada.");
52
- console.log('Parámetros recibidos para agregar firmante:', signerData);
91
+ export const getDigest = async (signData) => {
92
+ helpers.validateGetDigestData(signData);
93
+
94
+ const { integrationType } = getConfig();
95
+
96
+ if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
97
+ return getDigestToSignOnBoarding(signData);
98
+ }
53
99
 
54
- if (!signerData || typeof signerData !== 'object') {
55
- throw new Error('Datos de firmante son requeridos para addSigner.');
100
+ if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
101
+ return getDigestToSignOnPremise();
56
102
  }
57
103
 
104
+ throw new ApacuanaAPIError(
105
+ `Document retrieval is not supported for an unknown integration type: ${integrationType}`,
106
+ 501,
107
+ "NOT_IMPLEMENTED"
108
+ );
109
+ };
110
+
111
+ const addSignerOnBoarding = async (signerData) => {
112
+ helpers.validateOnBoardingSignerData(signerData);
58
113
  try {
59
- const response = await httpRequest(
60
- '/docs/signers',
61
- signerData,
62
- 'POST',
114
+ // Current httpRequest logic is moved here
115
+ await httpRequest("services/api/documents/signing", signerData, "POST");
116
+ return { signer: signerData.typedoc + signerData.doc, success: true };
117
+ } catch (error) {
118
+ if (error instanceof ApacuanaAPIError) {
119
+ throw error;
120
+ }
121
+ throw new Error(
122
+ `Failed to add signer in On-Boarding: ${error.message}`
123
+ );
124
+ }
125
+ };
126
+
127
+ const addSignerOnPremise = async () => {
128
+ throw new ApacuanaAPIError(
129
+ "Adding signers is not supported for integration type: ONPREMISE",
130
+ 501,
131
+ "NOT_IMPLEMENTED"
132
+ );
133
+ };
134
+
135
+ /**
136
+ * Adds a signer to a document, handling different integration types.
137
+ * @param {object} signerData - Data of the signer to be added.
138
+ * @returns {Promise<{signer: string, success: boolean}>} Object with the result of adding the signer.
139
+ * @throws {ApacuanaAPIError} If the signer data is invalid, if the API call fails, or if the integration type is not supported.
140
+ */
141
+ export const addSigner = async (signerData) => {
142
+ if (
143
+ !signerData ||
144
+ typeof signerData !== "object" ||
145
+ Object.keys(signerData).length === 0
146
+ ) {
147
+ throw new ApacuanaAPIError(
148
+ "Signer data (signerData) is required and must be a non-empty object.",
149
+ 400,
150
+ "INVALID_PARAMETER"
63
151
  );
64
- console.log(
65
- 'Respuesta del servidor simulada para agregar firmante:',
66
- response,
152
+ }
153
+
154
+ const { integrationType } = getConfig();
155
+
156
+ if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
157
+ return addSignerOnBoarding(signerData);
158
+ }
159
+
160
+ if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
161
+ return addSignerOnPremise(signerData);
162
+ }
163
+
164
+ throw new ApacuanaAPIError(
165
+ `Unsupported integration type: ${integrationType}`,
166
+ 400,
167
+ "UNSUPPORTED_INTEGRATION_TYPE"
168
+ );
169
+ };
170
+
171
+ const getDocsOnPremise = async () => {
172
+ throw new ApacuanaAPIError(
173
+ "Document retrieval is not supported for integration type: ONBOARDING",
174
+ 501,
175
+ "NOT_IMPLEMENTED"
176
+ );
177
+ };
178
+
179
+ const getDocsOnBoarding = async (data) => {
180
+ const { customerId } = getConfig();
181
+ if (!customerId) {
182
+ throw new ApacuanaAPIError(
183
+ "'customerId' is not configured. Please configure the SDK.",
184
+ 400,
185
+ "CONFIGURATION_ERROR"
67
186
  );
68
- if (!response.success) {
69
- throw new ApacuanaAPIError(
70
- response.message || 'Error desconocido al agregar firmante.',
71
- );
187
+ }
188
+
189
+ try {
190
+ const params = new URLSearchParams({
191
+ page: data.page,
192
+ customerid: customerId,
193
+ size: data.size,
194
+ });
195
+
196
+ if (typeof data.status !== "undefined") {
197
+ params.append("status", data.status);
72
198
  }
73
- return { signerId: response.id, status: response.message };
199
+
200
+ const apiUrl = `services/api/documents/listcustomer?${params.toString()}`;
201
+ const response = await httpRequest(apiUrl, {}, "GET");
202
+ return {
203
+ totalRecords: response.numofrecords,
204
+ records: response.records,
205
+ success: true,
206
+ };
74
207
  } catch (error) {
75
- throw new Error(`Fallo al agregar firmante: ${error.message}`);
208
+ if (error.name === "ApacuanaAPIError") {
209
+ throw error;
210
+ }
211
+ throw new ApacuanaAPIError(
212
+ `Failed to get document list (on-premise): ${error.message}`
213
+ );
214
+ }
215
+ };
216
+
217
+ /**
218
+ * Gets a list of documents.
219
+ * @param {object} data - Object with pagination parameters. Must contain {page, size}.
220
+ * @returns {Promise<{totalRecords: number, records:any[], success: boolean}>} Object with the list of documents.
221
+ * @throws {ApacuanaAPIError} If pagination parameters are invalid, if 'customerId' is not configured, if the API call fails, or if the integration type is not supported.
222
+ */
223
+ export const getDocs = async (data) => {
224
+ helpers.validateGetDocsData(data);
225
+
226
+ const { integrationType } = getConfig();
227
+
228
+ if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
229
+ return getDocsOnBoarding(data);
230
+ }
231
+
232
+ if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
233
+ return getDocsOnPremise();
76
234
  }
235
+
236
+ throw new ApacuanaAPIError(
237
+ `Document retrieval is not supported for an unknown integration type: ${integrationType}`,
238
+ 501,
239
+ "NOT_IMPLEMENTED"
240
+ );
77
241
  };
package/src/api/users.js CHANGED
@@ -7,46 +7,36 @@ import { getConfig } from "../config/index";
7
7
  * Este método es útil para endpoints que requieren datos en el cuerpo de la petición
8
8
  * para buscar un usuario, como un ID de sesión o un token de acceso.
9
9
  *
10
- * @returns {Promise<object>} Objeto con los detalles del usuario.
10
+ * @returns {Promise<{token: string, userData: object, success: boolean}>} Objeto con el token de sesión, los datos del usuario y un indicador de éxito.
11
11
  * @throws {Error} Si los parámetros de entrada son inválidos.
12
12
  * @throws {ApacuanaAPIError} Si ocurre un error en la API de Apacuana.
13
13
  */
14
14
  const getCustomer = async () => {
15
15
  const { verificationId, customerId } = getConfig();
16
+
17
+ if (!verificationId || !customerId) {
18
+ throw new ApacuanaAPIError(
19
+ "Both 'verificationId' and 'customerId' must be configured.",
20
+ 400,
21
+ "CONFIGURATION_ERROR"
22
+ );
23
+ }
24
+
16
25
  const body = {
17
26
  verificationid: verificationId,
18
27
  customerid: customerId,
19
28
  };
20
- // eslint-disable-next-line no-console
21
- console.log("-> Función 'getCustomer' ejecutada.");
22
- // eslint-disable-next-line no-console
23
- console.log("Parámetros recibidos para buscar usuario:", body);
24
-
25
- // 1. Validación de Parámetros de Entrada (Lado del SDK)
26
- if (!body || !body.verificationid || !body.customerid) {
27
- throw new Error(
28
- "El 'body' con 'userId' es un parámetro requerido para getCustomer."
29
- );
30
- }
31
29
 
32
30
  try {
33
- // 2. Realizar la Llamada al Backend a través del HttpClient
34
- // Usamos POST ya que la búsqueda podría requerir datos sensibles o complejos en el cuerpo.
35
31
  const response = await httpRequest(
36
- "services/api/register/init", // Endpoint de ejemplo
37
- body, // Los datos a enviar en el cuerpo de la petición
32
+ "services/api/register/init",
33
+ body,
38
34
  "POST"
39
35
  );
40
36
 
41
- // 3. Procesar la Respuesta del Backend
42
- // Asumiendo que la respuesta exitosa del backend incluye un objeto 'user'
43
- // eslint-disable-next-line no-console
44
- console.log("Respuesta del servidor para el token:", response);
45
-
46
37
  if (!response.sessionid || !response.entry) {
47
- // Si el backend no devuelve el objeto 'user' esperado
48
38
  throw new ApacuanaAPIError(
49
- "La respuesta de la API no contiene el usuario.",
39
+ "The API response does not contain the user.",
50
40
  200,
51
41
  "INVALID_API_RESPONSE"
52
42
  );
@@ -55,17 +45,15 @@ const getCustomer = async () => {
55
45
  return {
56
46
  token: response.sessionid,
57
47
  userData: response.entry,
58
- }; // Retorna el objeto de usuario directamente
48
+ success: true,
49
+ };
59
50
  } catch (error) {
60
- // 4. Manejo de Errores
61
- // eslint-disable-next-line no-console
62
- console.error("Error en getCustomer:", error);
63
51
  if (error instanceof ApacuanaAPIError) {
64
52
  throw error;
65
53
  }
66
54
  throw new ApacuanaAPIError(
67
- `Fallo inesperado al obtener el token: ${
68
- error.message || "Error desconocido"
55
+ `Unexpected failure getting token: ${
56
+ error.message || "Unknown error"
69
57
  }`
70
58
  );
71
59
  }
@@ -9,6 +9,7 @@ export class ApacuanaAPIError extends Error {
9
9
  this.name = "ApacuanaAPIError";
10
10
  this.statusCode = statusCode;
11
11
  this.errorCode = errorCode;
12
+ this.success = false; // Añadir el atributo success con valor false
12
13
 
13
14
  // Mantener el stack trace correcto
14
15
  if (Error.captureStackTrace) {
package/src/index.js CHANGED
@@ -1,9 +1,9 @@
1
- // src/index.js
2
1
  import { setConfig, getConfig } from "./config/index";
3
2
  import { initHttpClient, setAuthToken } from "./utils/httpClient";
4
3
  import getCustomer from "./api/users";
5
4
  import requestRevocation from "./api/revocations";
6
5
  import { getCertStatus } from "./api/certs";
6
+ import { addSigner, getDocs } from "./api/signatures";
7
7
 
8
8
  const apacuana = {
9
9
  /**
@@ -62,6 +62,9 @@ const apacuana = {
62
62
  getConfig,
63
63
  requestRevocation,
64
64
  getCertStatus,
65
+ getCustomer,
66
+ addSigner,
67
+ getDocs,
65
68
  };
66
69
 
67
70
  export default apacuana;