apacuana-sdk-core 1.26.9 → 1.26.11

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 (34) hide show
  1. package/README.md +28 -0
  2. package/coverage/clover.xml +50 -36
  3. package/coverage/coverage-final.json +4 -4
  4. package/coverage/lcov-report/index.html +25 -25
  5. package/coverage/lcov-report/src/api/certs.js.html +1 -1
  6. package/coverage/lcov-report/src/api/faceLiveness.js.html +1 -1
  7. package/coverage/lcov-report/src/api/index.html +18 -18
  8. package/coverage/lcov-report/src/api/revocations.js.html +1 -1
  9. package/coverage/lcov-report/src/api/signatures.js.html +1 -1
  10. package/coverage/lcov-report/src/api/users.js.html +130 -10
  11. package/coverage/lcov-report/src/config/index.html +1 -1
  12. package/coverage/lcov-report/src/config/index.js.html +1 -1
  13. package/coverage/lcov-report/src/errors/index.html +1 -1
  14. package/coverage/lcov-report/src/errors/index.js.html +8 -8
  15. package/coverage/lcov-report/src/index.html +14 -14
  16. package/coverage/lcov-report/src/index.js.html +24 -9
  17. package/coverage/lcov-report/src/success/index.html +1 -1
  18. package/coverage/lcov-report/src/success/index.js.html +5 -5
  19. package/coverage/lcov-report/src/utils/constant.js.html +1 -1
  20. package/coverage/lcov-report/src/utils/helpers.js.html +1 -1
  21. package/coverage/lcov-report/src/utils/httpClient.js.html +1 -1
  22. package/coverage/lcov-report/src/utils/index.html +1 -1
  23. package/coverage/lcov.info +98 -65
  24. package/dist/api/users.d.ts +5 -1
  25. package/dist/index.d.ts +1 -0
  26. package/dist/index.js +80 -16
  27. package/dist/index.js.map +1 -1
  28. package/dist/index.mjs +80 -16
  29. package/dist/index.mjs.map +1 -1
  30. package/package.json +1 -1
  31. package/src/api/users.js +40 -0
  32. package/src/index.js +6 -1
  33. package/tests/api/users.test.js +82 -1
  34. package/tests/integration/searchCustomer.test.js +75 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apacuana-sdk-core",
3
- "version": "1.26.9",
3
+ "version": "1.26.11",
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/users.js CHANGED
@@ -89,6 +89,46 @@ export const getCustomer = async () => {
89
89
  * @throws {ApacuanaAPIError} Si los datos de entrada son inválidos o si ocurre un error en la API.
90
90
  */
91
91
 
92
+ /**
93
+ * Busca clientes en el flujo de onboarding por correo y/o cédula.
94
+ * @param {object} params
95
+ * @param {string} [params.email] - Correo electrónico a buscar.
96
+ * @param {string} [params.doc] - Número de cédula a buscar.
97
+ * @returns {Promise<ApacuanaSuccess>} Resuelve con `{ customer }`, el registro del cliente encontrado.
98
+ * @throws {ApacuanaAPIError} Si la búsqueda es inválida o la API falla.
99
+ */
100
+ export const searchCustomer = async ({ email, doc } = {}) => {
101
+ if (!email && !doc) {
102
+ throw new ApacuanaAPIError(
103
+ "At least one of 'email' or 'doc' is required.",
104
+ 400,
105
+ "INVALID_SEARCH_QUERY"
106
+ );
107
+ }
108
+
109
+ const params = {};
110
+ if (email) params.email = email;
111
+ if (doc) params.doc = doc;
112
+
113
+ try {
114
+ const response = await httpRequest(
115
+ "services/api/onboardingclient/searchcustomers",
116
+ params,
117
+ "GET",
118
+ { skipAuth: true }
119
+ );
120
+
121
+ return new ApacuanaSuccess({ customer: response.entry ?? null });
122
+ } catch (error) {
123
+ if (error instanceof ApacuanaAPIError) {
124
+ throw error;
125
+ }
126
+ throw new ApacuanaAPIError(
127
+ `Unexpected failure searching customer: ${error.message || "Unknown error"}`
128
+ );
129
+ }
130
+ };
131
+
92
132
  export const createApacuanaUser = async (userData) => {
93
133
  try {
94
134
  const formData = new FormData();
package/src/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  import { sendFaceLiveness } from "./api/faceLiveness";
24
24
  import ApacuanaSuccess from "./success/index";
25
25
  import { ApacuanaAPIError } from "./errors/index";
26
- import { createApacuanaUser, getCustomer } from "./api/users";
26
+ import { createApacuanaUser, getCustomer, searchCustomer } from "./api/users";
27
27
 
28
28
  /**
29
29
  * Valida si el SDK está inicializado y si se dispone de un customerId.
@@ -159,6 +159,11 @@ const apacuana = {
159
159
  return getCustomer();
160
160
  },
161
161
 
162
+ searchCustomer: (data) => {
163
+ checkSdk(false);
164
+ return searchCustomer(data);
165
+ },
166
+
162
167
  createApacuanaUser: (data) => {
163
168
  checkSdk(false);
164
169
  return createApacuanaUser(data);
@@ -3,7 +3,7 @@ import { httpRequest } from "../../src/utils/httpClient.js";
3
3
  import { getConfig } from "../../src/config/index.js";
4
4
  import { ApacuanaAPIError } from "../../src/errors/index.js";
5
5
  import ApacuanaSuccess from "../../src/success/index.js";
6
- import { getCustomer } from "../../src/api/users.js";
6
+ import { getCustomer, searchCustomer } from "../../src/api/users.js";
7
7
 
8
8
  // Mockear las dependencias:
9
9
  jest.mock("../../src/utils/httpClient.js", () => ({
@@ -115,3 +115,84 @@ describe("getCustomer", () => {
115
115
  expect(httpRequest).toHaveBeenCalledTimes(1);
116
116
  });
117
117
  });
118
+
119
+ describe("searchCustomer", () => {
120
+ beforeEach(() => {
121
+ httpRequest.mockClear();
122
+ jest.spyOn(console, "error").mockImplementation(() => {});
123
+ });
124
+
125
+ afterEach(() => {
126
+ console.error.mockRestore();
127
+ });
128
+
129
+ test("should GET with email param and return the unwrapped customer entry", async () => {
130
+ httpRequest.mockResolvedValueOnce({ entry: { id: "abc", usr: "a@b.com" } });
131
+
132
+ const result = await searchCustomer({ email: "a@b.com" });
133
+
134
+ expect(httpRequest).toHaveBeenCalledWith(
135
+ "services/api/onboardingclient/searchcustomers",
136
+ { email: "a@b.com" },
137
+ "GET",
138
+ { skipAuth: true }
139
+ );
140
+ expect(result).toBeInstanceOf(ApacuanaSuccess);
141
+ expect(result.data).toEqual({ customer: { id: "abc", usr: "a@b.com" } });
142
+ });
143
+
144
+ test("should call with skipAuth: true so a Bearer token is not sent", async () => {
145
+ httpRequest.mockResolvedValueOnce({ entry: null });
146
+
147
+ await searchCustomer({ email: "a@b.com" });
148
+
149
+ // Public lookup — must not carry a Bearer token from the active session,
150
+ // otherwise the backend scopes the search to that session's customer
151
+ // and 404s for any other user.
152
+ const [, , , options] = httpRequest.mock.calls[0];
153
+ expect(options).toEqual({ skipAuth: true });
154
+ });
155
+
156
+ test("should send both email and doc when provided", async () => {
157
+ httpRequest.mockResolvedValueOnce({ entry: {} });
158
+
159
+ await searchCustomer({ email: "a@b.com", doc: "28152139" });
160
+
161
+ expect(httpRequest).toHaveBeenCalledWith(
162
+ "services/api/onboardingclient/searchcustomers",
163
+ { email: "a@b.com", doc: "28152139" },
164
+ "GET",
165
+ { skipAuth: true }
166
+ );
167
+ });
168
+
169
+ test("should return null customer when entry is absent", async () => {
170
+ httpRequest.mockResolvedValueOnce({});
171
+
172
+ const result = await searchCustomer({ doc: "28152139" });
173
+
174
+ expect(result.data).toEqual({ customer: null });
175
+ });
176
+
177
+ test("should throw without calling the API when no email or doc is given", async () => {
178
+ await expect(searchCustomer({})).rejects.toThrow(
179
+ "At least one of 'email' or 'doc' is required."
180
+ );
181
+ expect(httpRequest).not.toHaveBeenCalled();
182
+ });
183
+
184
+ test("should rethrow ApacuanaAPIError from the API", async () => {
185
+ const apiError = new ApacuanaAPIError("Bad request", 400, "API_ERROR");
186
+ httpRequest.mockRejectedValueOnce(apiError);
187
+
188
+ await expect(searchCustomer({ doc: "1" })).rejects.toThrow(apiError);
189
+ });
190
+
191
+ test("should wrap unexpected errors", async () => {
192
+ httpRequest.mockRejectedValueOnce(new Error("boom"));
193
+
194
+ await expect(searchCustomer({ doc: "1" })).rejects.toThrow(
195
+ "Unexpected failure searching customer: boom"
196
+ );
197
+ });
198
+ });
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Integration test for searchCustomer
3
+ *
4
+ * Requires environment variables (copy .env.example to .env and fill in):
5
+ * APACUANA_API_URL=https://api.dev.apacuana.3dlinkweb.com
6
+ * APACUANA_SECRET_KEY=your-secret-key
7
+ * APACUANA_API_KEY=your-api-key
8
+ * APACUANA_VERIFICATION_ID=your-verification-id
9
+ * APACUANA_SEARCH_EMAIL=samuel_dear@hotmail.com (or APACUANA_SEARCH_DOC)
10
+ *
11
+ * Run with: NODE_ENV=test npm test -- --testPathPatterns="integration/searchCustomer.test.js"
12
+ */
13
+ import { readFileSync } from "fs";
14
+ import { resolve } from "path";
15
+
16
+ const envPath = resolve(process.cwd(), ".env");
17
+
18
+ try {
19
+ const envContent = readFileSync(envPath, "utf-8");
20
+ envContent.split("\n").forEach(line => {
21
+ const [key, ...valueParts] = line.split("=");
22
+ if (key && key.trim() && !key.startsWith("#")) {
23
+ process.env[key.trim()] = valueParts.join("=").replace(/"/g, "").trim();
24
+ }
25
+ });
26
+ } catch (err) {
27
+ // .env file not found, continue without it
28
+ }
29
+
30
+ import apacuana from "../../src/index.js";
31
+
32
+ const requiredEnvVars = [
33
+ "APACUANA_API_URL",
34
+ "APACUANA_SECRET_KEY",
35
+ "APACUANA_API_KEY",
36
+ "APACUANA_VERIFICATION_ID",
37
+ ];
38
+
39
+ const missingVars = requiredEnvVars.filter(v => !process.env[v]);
40
+ if (missingVars.length > 0) {
41
+ test.skip("searchCustomer integration", () => {});
42
+ throw new Error(
43
+ `\n⚠️ Missing environment variables: ${missingVars.join(", ")}\n` +
44
+ "Create a .env file based on .env.example.\n"
45
+ );
46
+ }
47
+
48
+ describe("searchCustomer - Integration", () => {
49
+ beforeAll(async () => {
50
+ await apacuana.init({
51
+ apiUrl: process.env.APACUANA_API_URL,
52
+ secretKey: process.env.APACUANA_SECRET_KEY,
53
+ apiKey: process.env.APACUANA_API_KEY,
54
+ verificationId: process.env.APACUANA_VERIFICATION_ID,
55
+ integrationType: process.env.APACUANA_INTEGRATION_TYPE || "ONBOARDING",
56
+ });
57
+ });
58
+
59
+ afterAll(() => {
60
+ apacuana.close();
61
+ });
62
+
63
+ test("should search a customer by email or doc and return a result object", async () => {
64
+ const query = process.env.APACUANA_SEARCH_DOC
65
+ ? { doc: process.env.APACUANA_SEARCH_DOC || "28152139" }
66
+ : { email: process.env.APACUANA_SEARCH_EMAIL || "samuel_dear@hotmail.com" };
67
+
68
+ const result = await apacuana.searchCustomer(query);
69
+
70
+ expect(result).toBeDefined();
71
+ expect(result.success).toBe(true);
72
+ // customer is the unwrapped entry, or null when there is no match
73
+ expect(result.data).toHaveProperty("customer");
74
+ });
75
+ });