All files / api signatures.js

50.6% Statements 42/83
38.46% Branches 15/39
46.15% Functions 6/13
50.6% Lines 42/83

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337                                                                                                                                                        1x                                                                 1x               1x                                                         1x               1x 1x 1x 1x 1x                 1x 1x             1x 1x             1x 2x 2x 1x             1x 1x           1x 1x     1x 1x 1x                                                   1x                                               1x                                                   1x 4x         1x           3x   3x 1x     2x 1x     1x                         1x 5x   3x   3x 2x     1x 1x                  
// src/api/signatures.js
import { getConfig } from "../config/index";
import { httpRequest } from "../utils/httpClient";
import { ApacuanaAPIError } from "../errors";
import helpers from "../utils/helpers";
import { INTEGRATION_TYPE } from "../utils/constant";
 
// =================================================================
// Type Definitions
// =================================================================
 
/**
 * Define la estructura de un objeto Firmante.
 * @typedef {object} Signer
 * @property {string} name - Nombre completo del firmante.
 * @property {string} email - Correo electrónico del firmante.
 * @property {string} document - Documento de identidad del firmante.
 */
 
/**
 * Define la estructura de datos para añadir un firmante.
 * @typedef {object} SignerData
 * @property {string} docId - Identificador único del documento.
 * @property {Signer} signer - Objeto con la información del firmante.
 */
 
/**
 * Define la estructura de la respuesta al añadir un firmante.
 * @typedef {object} AddSignerResponse
 * @property {string} signer - Identificador de confirmación del firmante añadido.
 * @property {boolean} success - Indica si la operación fue exitosa.
 */
 
/**
 * Define la estructura de datos para obtener el digest de un documento.
 * @typedef {object} GetDigestData
 * @property {string} cert - Certificado del firmante en formato base64.
 * @property {string} signatureId - Identificador único del proceso de firma.
 */
 
/**
 * Define la estructura de la respuesta al obtener el digest.
 * @typedef {object} GetDigestResponse
 * @property {string} digest - El digest del documento que se va a firmar.
 * @property {boolean} success - Indica si la operación fue exitosa.
 */
 
/**
 * Define la estructura de los parámetros para obtener documentos.
 * @typedef {object} GetDocsParams
 * @property {number} page - Número de página para la paginación.
 * @property {number} size - Cantidad de registros por página.
 * @property {string} [status] - (Opcional) Estado para filtrar los documentos.
 */
 
/**
 * Define la estructura de la respuesta al obtener documentos.
 * @typedef {object} GetDocsResponse
 * @property {number} totalRecords - El número total de registros encontrados.
 * @property {Array<object>} records - Un arreglo con los registros de los documentos.
 * @property {boolean} success - Indica si la operación fue exitosa.
 */
 
/**
 * @typedef {object} SignDocumentData
 * @property {object} signature - Objeto con información de la firma.
 * @property {string} signature.id - ID de la firma.
 * @property {Array<object>} signature.positions - Posiciones de la firma.
 * @property {string} cert - Certificado en base64.
 * @property {string} signedDigest - Digest firmado.
 */
 
// =================================================================
// Internal Functions
// =================================================================
 
const signDocumentOnBoarding = async (signData) => {
  helpers.validateOnBoardingSignDocumentData(signData);
  const { signature, cert, signedDigest } = signData;
  try {
    const signBody = {
      positions: JSON.stringify(
        signature.positions.map((p) => ({
          x: p.x,
          y: p.y,
          page: p.page,
          status: 1,
        }))
      ),
      publickey: cert,
      signeddigest: signedDigest,
    };
 
    const response = await httpRequest(
      `services/api/documents/sign/${signature.id}`,
      signBody,
      "PUT"
    );
    return response;
  } catch (error) {
    if (error instanceof ApacuanaAPIError) {
      throw error;
    }
    throw new ApacuanaAPIError(
      `Failed to sign document: ${error.message || "Unknown error"}`
    );
  }
};
 
const signDocumentOnPremise = async () => {
  throw new ApacuanaAPIError(
    "Document signing is not supported for integration type: ONPREMISE",
    501,
    "NOT_IMPLEMENTED"
  );
};
 
const getDigestToSignOnBoarding = async (signData) => {
  try {
    const response = await httpRequest(
      `services/api/documents/getdigest/${signData.signatureId}`,
      {
        publickey: signData.cert,
      },
      "POST"
    );
    const digest = response.data?.digest || response.digest;
    if (!digest) {
      throw new ApacuanaAPIError(
        "Signature generation failed: digest not found in the response.",
        500,
        "API_RESPONSE_ERROR"
      );
    }
 
    return { digest, success: true };
  } catch (error) {
    if (error.name === "ApacuanaAPIError") {
      throw error;
    }
    throw new ApacuanaAPIError(
      `Failed to sign document (on-boarding): ${error.message}`
    );
  }
};
 
const getDigestToSignOnPremise = async () => {
  throw new ApacuanaAPIError(
    "Digest retrieval is not supported for integration type: ONPREMISE",
    501,
    "NOT_IMPLEMENTED"
  );
};
 
const addSignerOnBoarding = async (signerData) => {
  helpers.validateOnBoardingSignerData(signerData);
  try {
    await httpRequest("services/api/documents/signing", signerData, "POST");
    return { signer: signerData.typedoc + signerData.doc, success: true };
  } catch (error) {
    if (error instanceof ApacuanaAPIError) {
      throw error;
    }
    throw new Error(`Failed to add signer in On-Boarding: ${error.message}`);
  }
};
 
const addSignerOnPremise = async () => {
  throw new ApacuanaAPIError(
    "Adding signers is not supported for integration type: ONPREMISE",
    501,
    "NOT_IMPLEMENTED"
  );
};
 
const getDocsOnPremise = async () => {
  throw new ApacuanaAPIError(
    "Document retrieval is not supported for integration type: ONPREMISE",
    501,
    "NOT_IMPLEMENTED"
  );
};
 
const getDocsOnBoarding = async (data) => {
  const { customerId } = getConfig();
  if (!customerId) {
    throw new ApacuanaAPIError(
      "'customerId' is not configured. Please configure the SDK.",
      400,
      "CONFIGURATION_ERROR"
    );
  }
 
  try {
    const params = new URLSearchParams({
      page: data.page,
      customerid: customerId,
      size: data.size,
    });
 
    Eif (typeof data.status !== "undefined") {
      params.append("status", data.status);
    }
 
    const apiUrl = `services/api/documents/listcustomer?${params.toString()}`;
    const response = await httpRequest(apiUrl, {}, "GET");
    return {
      totalRecords: response.numofrecords,
      records: response.records,
      success: true,
    };
  } catch (error) {
    if (error.name === "ApacuanaAPIError") {
      throw error;
    }
    throw new ApacuanaAPIError(
      `Failed to get document list (on-boarding): ${error.message}`
    );
  }
};
 
// =================================================================
// Exported Functions
// =================================================================
 
/**
 * Firma un documento PDF con un certificado digital.
 * NOTA: Esta función no está implementada actualmente para ningún tipo de integración.
 * @param {SignDocumentData} signData - Datos necesarios para la firma.
 * @returns {Promise<object>}
 * @throws {ApacuanaAPIError} Arroja un error 'NOT_IMPLEMENTED' para cualquier tipo de integración.
 */
export const signDocument = async (signData) => {
  const { integrationType } = getConfig();
 
  if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
    return signDocumentOnBoarding(signData);
  }
 
  if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
    return signDocumentOnPremise();
  }
 
  throw new ApacuanaAPIError(
    `Unsupported integration type: ${integrationType}`,
    400,
    "UNSUPPORTED_INTEGRATION_TYPE"
  );
};
 
/**
 * Obtiene el digest de un documento para ser firmado.
 * @param {GetDigestData} signData - Datos requeridos para obtener el digest.
 * @returns {Promise<GetDigestResponse>} Una promesa que resuelve a un objeto con el digest del documento.
 * @throws {ApacuanaAPIError} Si los datos de entrada son inválidos, la llamada a la API falla, o el tipo de integración no es soportado.
 */
export const getDigest = async (signData) => {
  helpers.validateGetDigestData(signData);
 
  const { integrationType } = getConfig();
 
  if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
    return getDigestToSignOnBoarding(signData);
  }
 
  if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
    return getDigestToSignOnPremise();
  }
 
  throw new ApacuanaAPIError(
    `Document retrieval is not supported for an unknown integration type: ${integrationType}`,
    501,
    "NOT_IMPLEMENTED"
  );
};
 
/**
 * Añade un firmante a un documento.
 * @param {SignerData} signerData - Los datos del firmante que se va a añadir.
 * @returns {Promise<AddSignerResponse>} Una promesa que resuelve a un objeto con el resultado de la operación.
 * @throws {ApacuanaAPIError} Si los datos del firmante son inválidos, la llamada a la API falla, o el tipo de integración no es soportado.
 */
export const addSigner = async (signerData) => {
  if (
    !signerData ||
    typeof signerData !== "object" ||
    Object.keys(signerData).length === 0
  ) {
    throw new ApacuanaAPIError(
      "Signer data (signerData) is required and must be a non-empty object.",
      400,
      "INVALID_PARAMETER"
    );
  }
  const { integrationType } = getConfig();
 
  if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
    return addSignerOnBoarding(signerData);
  }
 
  if (integrationType === INTEGRATION_TYPE.ONPREMISE) {
    return addSignerOnPremise();
  }
 
  throw new ApacuanaAPIError(
    `Unsupported integration type: ${integrationType}`,
    400,
    "UNSUPPORTED_INTEGRATION_TYPE"
  );
};
 
/**
 * Obtiene una lista de documentos basada en los filtros especificados.
 * @param {GetDocsParams} data - Objeto con parámetros de paginación y filtros.
 * @returns {Promise<GetDocsResponse>} Una promesa que resuelve a un objeto con la lista de documentos.
 * @throws {ApacuanaAPIError} Si los parámetros de paginación son inválidos, 'customerId' no está configurado, la llamada a la API falla, o el tipo de integración no es soportado.
 */
export const getDocs = async (data) => {
  helpers.validateGetDocsData(data);
 
  const { integrationType } = getConfig();
 
  if (integrationType === INTEGRATION_TYPE.ONBOARDING) {
    return getDocsOnBoarding(data);
  }
 
  Eif (integrationType === INTEGRATION_TYPE.ONPREMISE) {
    return getDocsOnPremise();
  }
 
  throw new ApacuanaAPIError(
    `Document retrieval is not supported for an unknown integration type: ${integrationType}`,
    501,
    "NOT_IMPLEMENTED"
  );
};