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.
- package/README.md +28 -0
- package/coverage/clover.xml +50 -36
- package/coverage/coverage-final.json +4 -4
- package/coverage/lcov-report/index.html +25 -25
- package/coverage/lcov-report/src/api/certs.js.html +1 -1
- package/coverage/lcov-report/src/api/faceLiveness.js.html +1 -1
- package/coverage/lcov-report/src/api/index.html +18 -18
- package/coverage/lcov-report/src/api/revocations.js.html +1 -1
- package/coverage/lcov-report/src/api/signatures.js.html +1 -1
- package/coverage/lcov-report/src/api/users.js.html +130 -10
- package/coverage/lcov-report/src/config/index.html +1 -1
- package/coverage/lcov-report/src/config/index.js.html +1 -1
- package/coverage/lcov-report/src/errors/index.html +1 -1
- package/coverage/lcov-report/src/errors/index.js.html +8 -8
- package/coverage/lcov-report/src/index.html +14 -14
- package/coverage/lcov-report/src/index.js.html +24 -9
- package/coverage/lcov-report/src/success/index.html +1 -1
- package/coverage/lcov-report/src/success/index.js.html +5 -5
- package/coverage/lcov-report/src/utils/constant.js.html +1 -1
- package/coverage/lcov-report/src/utils/helpers.js.html +1 -1
- package/coverage/lcov-report/src/utils/httpClient.js.html +1 -1
- package/coverage/lcov-report/src/utils/index.html +1 -1
- package/coverage/lcov.info +98 -65
- package/dist/api/users.d.ts +5 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +80 -16
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +80 -16
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/api/users.js +40 -0
- package/src/index.js +6 -1
- package/tests/api/users.test.js +82 -1
- package/tests/integration/searchCustomer.test.js +75 -0
package/package.json
CHANGED
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);
|
package/tests/api/users.test.js
CHANGED
|
@@ -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
|
+
});
|