@fiado/api-invoker 5.66.0 → 5.68.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/bin/datacultrConnector/api/interfaces/IDatacultrConnectorApi.d.ts +2 -16
- package/bin/datacultrConnector/api/interfaces/IDatacultrConnectorApi.js +49 -1
- package/bin/index.d.ts +1 -0
- package/bin/index.js +1 -0
- package/bin/loanCredit/api/LoanCreditDeviceApi.d.ts +4 -1
- package/bin/loanCredit/api/LoanCreditDeviceApi.js +17 -0
- package/bin/loanCredit/api/interfaces/ILoanCreditDeviceApi.d.ts +35 -7
- package/bin/retailCatalog/api/RetailCatalogBusinessApi.d.ts +3 -2
- package/bin/retailCatalog/api/RetailCatalogBusinessApi.js +4 -10
- package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.d.ts +17 -48
- package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.js +0 -2
- package/bin/trustonicConnector/api/TrustonicConnectorApi.d.ts +34 -0
- package/bin/trustonicConnector/api/TrustonicConnectorApi.js +73 -0
- package/bin/trustonicConnector/api/interfaces/ITrustonicConnectorApi.d.ts +158 -0
- package/bin/trustonicConnector/api/interfaces/ITrustonicConnectorApi.js +1 -0
- package/bin/trustonicConnector/index.d.ts +2 -0
- package/bin/trustonicConnector/index.js +2 -0
- package/bin/utils/MdmEnvelopes.d.ts +17 -0
- package/bin/utils/MdmEnvelopes.js +6 -0
- package/package.json +2 -2
- package/src/datacultrConnector/api/interfaces/IDatacultrConnectorApi.ts +4 -18
- package/src/index.ts +1 -0
- package/src/loanCredit/api/LoanCreditDeviceApi.ts +30 -0
- package/src/loanCredit/api/interfaces/ILoanCreditDeviceApi.ts +46 -6
- package/src/retailCatalog/api/RetailCatalogBusinessApi.ts +12 -14
- package/src/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.ts +25 -52
- package/src/trustonicConnector/api/TrustonicConnectorApi.ts +118 -0
- package/src/trustonicConnector/api/interfaces/ITrustonicConnectorApi.ts +216 -0
- package/src/trustonicConnector/index.ts +2 -0
- package/src/utils/MdmEnvelopes.ts +20 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
|
+
import { DeviceAutoLockScheduleRequest, DeviceAutoLockScheduleResponse, DeviceEnrollInitiateResponse, DeviceEnrollRequest, DeviceEnrollStatusRequest, DeviceEnrollStatusResponse, DeviceLastSeenRequest, DeviceLastSeenResponse, DeviceLockRequest, DeviceLockResponse, DeviceNotifyRequest, DeviceNotifyResponse, DevicePinUnlockRequest, DevicePinUnlockResponse, DeviceReleaseInitiateResponse, DeviceReleaseRequest, DeviceReleaseStatusRequest, DeviceReleaseStatusResponse, DeviceStatusRequest, DeviceStatusResponse, DeviceUnlockRequest, DeviceUnlockResponse, MdmProviderEnum } from "@fiado/type-kit/bin/mdm/index.js";
|
|
3
|
+
import { DeviceBatchRequest, DeviceBatchResult } from "../../../utils/MdmEnvelopes.js";
|
|
4
|
+
/**
|
|
5
|
+
* Contrato del publisher HTTP del lambda `trustonic-connector` — el segundo provider MDM de
|
|
6
|
+
* SureKeep, que traduce el contrato agnóstico de dispositivo (`@fiado/type-kit/bin/mdm`) a la API
|
|
7
|
+
* de Trustonic. Su consumidor es el módulo `device/` de `loan-credit-business`, que NUNCA le pega
|
|
8
|
+
* por `http-client` directo: entre lambdas Fiado se habla siempre por `@fiado/api-invoker`.
|
|
9
|
+
*
|
|
10
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
+
* SUPERFICIE — 11 operaciones, las mismas que `IDatacultrConnectorApi`.
|
|
12
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
13
|
+
* El connector expone 16 endpoints. Quedan afuera `restrictApps`, `archive`, `deactivate`,
|
|
14
|
+
* `deactivateStatus` y `deviceLog`: `IMdmProviderAdapter` de `loan-credit-business` no los pide y
|
|
15
|
+
* el adapter tiene un solo contrato para los dos providers. Sumar operaciones que solo soporta
|
|
16
|
+
* Trustonic es un cambio aparte, con su propia decisión.
|
|
17
|
+
*
|
|
18
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
* PATHS — `/devices/...`, SIN prefijo `/private`. Igual que Datacultr.
|
|
20
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
* El aislamiento no lo da el prefijo sino la red: la API privada es `EndpointConfiguration:
|
|
22
|
+
* PRIVATE` con ResourcePolicy que deniega todo lo que no venga de los VPC Endpoints de Fiado.
|
|
23
|
+
*
|
|
24
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
25
|
+
* SIN TENANT y `Feature.ANONIMUS`.
|
|
26
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
27
|
+
* Ningún método lleva `tenantId`: el `PrivateController` del destino no lee `X-Tenant-Id` ni
|
|
28
|
+
* `x-tenant-issuer`, y las credenciales de Trustonic salen de un único secret del lambda. Los
|
|
29
|
+
* privados son `Feature.ANONIMUS` — sin RBAC y sin claims, así que el caller no manda
|
|
30
|
+
* `Authorization`. Misma deuda que Datacultr: cuando el MDM sea multi-tenant, este contrato suma
|
|
31
|
+
* el tenant y pasa a ser un cambio MAJOR.
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ Retorno `StandardResponse<T>` — el consumer lee `result.data`, NO `result.body`.
|
|
34
|
+
* Errores de transporte (falla el lambda entero, no un ítem): el publisher RECHAZA con el
|
|
35
|
+
* `DomainError` tipado del destino. Nunca fallback: el caller jamás recibe un batch vacío
|
|
36
|
+
* disfrazado de éxito.
|
|
37
|
+
*
|
|
38
|
+
* Granularidad POR ÍTEM — el batch NUNCA falla entero:
|
|
39
|
+
* - Cada `results[i]` trae su propio `status` (`SUCCESS` | `PENDING` | `ERROR`) y un `error`
|
|
40
|
+
* opcional. Un 200 puede traer ítems en `ERROR`: el caller TIENE que recorrer `results`.
|
|
41
|
+
* - El `error.code` del ítem es un `MdmErrorCodeEnum`. El `status` no lleva el motivo.
|
|
42
|
+
*
|
|
43
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
44
|
+
* DIFERENCIAS DE CONTRATO CONTRA DATACULTR — no son intercambiables.
|
|
45
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
46
|
+
* 1. `enrollDevices` devuelve `pollingHandle` + `provider` además de `results`; en Datacultr solo
|
|
47
|
+
* devuelve `results`. Acá el enroll es async de verdad y el handle es obligatorio para polear.
|
|
48
|
+
* 2. `enrollStatus` y `releaseStatus` EXIGEN `pollingHandle` en el body. En Datacultr el sobre de
|
|
49
|
+
* entrada es solo `{ items }`.
|
|
50
|
+
* 3. `lockDevices` soporta `TEMPORARY` de verdad; Datacultr lo degrada por ítem.
|
|
51
|
+
* 4. `autoLockSchedule` está degradado en Trustonic; en Datacultr es la vía real del bloqueo.
|
|
52
|
+
* 5. `notifyDevices` usa `title` + `content` (texto crudo) e ignora `notificationCode`; en
|
|
53
|
+
* Datacultr es al revés.
|
|
54
|
+
*
|
|
55
|
+
* Env var requerida en el consumer: `TRUSTONIC_CONNECTOR_URL`.
|
|
56
|
+
* El template.yml del consumer la setea con:
|
|
57
|
+
*
|
|
58
|
+
* TRUSTONIC_CONNECTOR_URL: '{{resolve:ssm:trustonic-connector}}'
|
|
59
|
+
*
|
|
60
|
+
* Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
|
|
61
|
+
*/
|
|
62
|
+
/**
|
|
63
|
+
* Sobre de entrada de los endpoints `/status` de Trustonic: además de los ítems exige el
|
|
64
|
+
* `pollingHandle` que devolvió el initiate. Es un string opaco — no lo inspecciones.
|
|
65
|
+
*/
|
|
66
|
+
export interface DevicePollingBatchRequest<TItem> {
|
|
67
|
+
pollingHandle: string;
|
|
68
|
+
items: TItem[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Sobre de salida de un initiate async de Trustonic. `pollingHandle` es lo que el caller manda
|
|
72
|
+
* después a `/status`; `provider` identifica qué MDM atendió la operación.
|
|
73
|
+
*/
|
|
74
|
+
export interface DeviceInitiateBatchResult<TItem> {
|
|
75
|
+
pollingHandle: string;
|
|
76
|
+
provider: MdmProviderEnum;
|
|
77
|
+
results: TItem[];
|
|
78
|
+
}
|
|
79
|
+
export interface ITrustonicConnectorApi {
|
|
80
|
+
/**
|
|
81
|
+
* POST `/devices/enroll` — inicia el enrolamiento de cada IMEI en Trustonic (upload de
|
|
82
|
+
* inventario + activación del servicio `DeviceFinancing` en una call atómica).
|
|
83
|
+
*
|
|
84
|
+
* ASYNC de verdad, a diferencia de Datacultr: el destino devuelve `pollingHandle` y el caller
|
|
85
|
+
* DEBE polear `enrollStatus` con ese handle hasta que todos los ítems sean terminales. El caso
|
|
86
|
+
* típico es que todos vuelvan en `PENDING`.
|
|
87
|
+
*
|
|
88
|
+
* `DeviceEnrollRequest.lockMode` (`ONDEMAND` pospago / `SCHEDULED` prepago) es opcional; si se
|
|
89
|
+
* omite el destino asume `ONDEMAND`.
|
|
90
|
+
*/
|
|
91
|
+
enrollDevices(input: DeviceBatchRequest<DeviceEnrollRequest>): Promise<StandardResponse<DeviceInitiateBatchResult<DeviceEnrollInitiateResponse>>>;
|
|
92
|
+
/**
|
|
93
|
+
* POST `/devices/enroll/status` — estado del enrolamiento iniciado con `enrollDevices`.
|
|
94
|
+
*
|
|
95
|
+
* ⚠️ EXIGE `pollingHandle`: sin él el destino responde 400 `MISSING_POLLING_HANDLE`.
|
|
96
|
+
*
|
|
97
|
+
* Los campos `enrolledAt`, `enrollmentMode`, `enrollmentId`, `brand` y `model` siempre vienen
|
|
98
|
+
* `undefined` — el endpoint de Trustonic no los expone; existen para Datacultr.
|
|
99
|
+
*/
|
|
100
|
+
enrollStatus(input: DevicePollingBatchRequest<DeviceEnrollStatusRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceEnrollStatusResponse>>>;
|
|
101
|
+
/** POST `/devices/status` — estado actual de cada IMEI, con `brand`, `model` y `lastSeenAt`. */
|
|
102
|
+
getStatus(input: DeviceBatchRequest<DeviceStatusRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceStatusResponse>>>;
|
|
103
|
+
/** POST `/devices/last-seen` — último contacto reportado por cada IMEI (heartbeat). */
|
|
104
|
+
getLastSeen(input: DeviceBatchRequest<DeviceLastSeenRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceLastSeenResponse>>>;
|
|
105
|
+
/**
|
|
106
|
+
* POST `/devices/lock` — bloquea cada IMEI, con mensaje opcional en pantalla.
|
|
107
|
+
* Acepta `mode: TEMPORARY` de verdad (con `durationSeconds`), que Datacultr degrada por ítem.
|
|
108
|
+
*/
|
|
109
|
+
lockDevices(input: DeviceBatchRequest<DeviceLockRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceLockResponse>>>;
|
|
110
|
+
/**
|
|
111
|
+
* POST `/devices/unlock` — desbloquea cada IMEI.
|
|
112
|
+
* `unlockUntil` (ISO-8601) acota hasta cuándo vale el desbloqueo y solo aplica en modo
|
|
113
|
+
* `SCHEDULED`; si el modo lo exige y falta, el ítem vuelve en `ERROR` con
|
|
114
|
+
* `MdmErrorCodeEnum.UNLOCK_UNTIL_REQUIRED`.
|
|
115
|
+
*/
|
|
116
|
+
unlockDevices(input: DeviceBatchRequest<DeviceUnlockRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceUnlockResponse>>>;
|
|
117
|
+
/**
|
|
118
|
+
* POST `/devices/auto-lock-schedule` — programa el bloqueo de cada IMEI en una fecha futura.
|
|
119
|
+
*
|
|
120
|
+
* ⚠️ DEGRADADO en Trustonic: el destino no llama al proveedor y devuelve cada ítem en `ERROR`
|
|
121
|
+
* con `PROVIDER_NOT_SUPPORTED`. Es el espejo exacto de Datacultr, donde ésta es la vía real del
|
|
122
|
+
* bloqueo. El caller no debe asumir la misma semántica entre providers.
|
|
123
|
+
*/
|
|
124
|
+
autoLockSchedule(input: DeviceBatchRequest<DeviceAutoLockScheduleRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceAutoLockScheduleResponse>>>;
|
|
125
|
+
/**
|
|
126
|
+
* POST `/devices/pin-unlock` — pide a Trustonic el PIN offline de un equipo sin conectividad.
|
|
127
|
+
*
|
|
128
|
+
* ⚠️ NO ES BATCH y NO LLEVA SOBRE, a diferencia del resto del contrato: el body es el
|
|
129
|
+
* `DevicePinUnlockRequest` pelado y la respuesta es el `DevicePinUnlockResponse` directo en
|
|
130
|
+
* `data`, sin `{ result }` — ahí difiere del `pinUnlock` de Datacultr.
|
|
131
|
+
*
|
|
132
|
+
* Operación sensible: `pin`, `validitySeconds` y `generatedAt` solo vienen con `status`
|
|
133
|
+
* `SUCCESS`. El PIN viaja únicamente en esta respuesta — nunca loggearlo.
|
|
134
|
+
*/
|
|
135
|
+
pinUnlock(input: DevicePinUnlockRequest): Promise<StandardResponse<DevicePinUnlockResponse>>;
|
|
136
|
+
/**
|
|
137
|
+
* POST `/devices/notify` — manda una notificación (`BANNER` o `FULLSCREEN`) a cada IMEI.
|
|
138
|
+
* Trustonic usa `title` y `content` como texto crudo e ignora `notificationCode`, al revés que
|
|
139
|
+
* Datacultr, que resuelve el texto por catálogo.
|
|
140
|
+
*/
|
|
141
|
+
notifyDevices(input: DeviceBatchRequest<DeviceNotifyRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceNotifyResponse>>>;
|
|
142
|
+
/**
|
|
143
|
+
* POST `/devices/release` — libera definitivamente cada IMEI del MDM (fin de tenure).
|
|
144
|
+
*
|
|
145
|
+
* ⚠️ IRREVERSIBLE: un equipo liberado no se puede re-enrolar en Trustonic.
|
|
146
|
+
* Es sync y terminal — cada ítem vuelve `SUCCESS` o `ERROR`, nunca `PENDING`, y la respuesta NO
|
|
147
|
+
* trae `pollingHandle` (asimetría con `enrollDevices`).
|
|
148
|
+
*/
|
|
149
|
+
releaseDevices(input: DeviceBatchRequest<DeviceReleaseRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceReleaseInitiateResponse>>>;
|
|
150
|
+
/**
|
|
151
|
+
* POST `/devices/release/status` — consulta el estado de una liberación.
|
|
152
|
+
*
|
|
153
|
+
* ⚠️ EXIGE `pollingHandle` aunque `releaseDevices` no devuelva ninguno: el caller tiene que
|
|
154
|
+
* inventar o reusar un identificador de tracing. Existe solo por simetría con Datacultr, donde
|
|
155
|
+
* el release sí es async; contra Trustonic normalmente no hace falta llamarlo.
|
|
156
|
+
*/
|
|
157
|
+
releaseStatus(input: DevicePollingBatchRequest<DeviceReleaseStatusRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceReleaseStatusResponse>>>;
|
|
158
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sobres de request/response compartidos por los connectors MDM (`datacultr-connector`,
|
|
3
|
+
* `trustonic-connector`). No viven en `@fiado/type-kit/bin/mdm` porque los DTOs publicados
|
|
4
|
+
* son de UN imei; el sobre es dominio del líder (gap E8), migrar a type-kit cuando lo publique.
|
|
5
|
+
*/
|
|
6
|
+
/** Sobre de entrada de los endpoints batch de un connector MDM. */
|
|
7
|
+
export interface DeviceBatchRequest<TItem> {
|
|
8
|
+
items: TItem[];
|
|
9
|
+
}
|
|
10
|
+
/** Sobre de salida de los endpoints batch: viaja dentro de `StandardResponse.data`. */
|
|
11
|
+
export interface DeviceBatchResult<TItem> {
|
|
12
|
+
results: TItem[];
|
|
13
|
+
}
|
|
14
|
+
/** Sobre de salida de los endpoints de UN solo IMEI. Hoy solo `pinUnlock` no es batch. */
|
|
15
|
+
export interface DeviceSingleResult<TItem> {
|
|
16
|
+
result: TItem;
|
|
17
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sobres de request/response compartidos por los connectors MDM (`datacultr-connector`,
|
|
3
|
+
* `trustonic-connector`). No viven en `@fiado/type-kit/bin/mdm` porque los DTOs publicados
|
|
4
|
+
* son de UN imei; el sobre es dominio del líder (gap E8), migrar a type-kit cuando lo publique.
|
|
5
|
+
*/
|
|
6
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiado/api-invoker",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.68.0",
|
|
4
4
|
"description": "Sirve como un puente entre diferentes funciones lambda, facilitando la comunicación entre ellas a través de invocaciones http",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@fiado/gateway-adapter": "^3.9.0",
|
|
35
35
|
"@fiado/http-client": "^2.0.1",
|
|
36
36
|
"@fiado/logger": "^1.1.3",
|
|
37
|
-
"@fiado/type-kit": "^3.
|
|
37
|
+
"@fiado/type-kit": "^3.400.0",
|
|
38
38
|
"dotenv": "^16.4.7"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
DeviceUnlockRequest,
|
|
24
24
|
DeviceUnlockResponse,
|
|
25
25
|
} from "@fiado/type-kit/bin/mdm/index.js";
|
|
26
|
+
import { DeviceBatchRequest, DeviceBatchResult, DeviceSingleResult } from "../../../utils/MdmEnvelopes.js";
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Contrato del publisher HTTP del lambda `datacultr-connector` — el provider MDM que traduce el
|
|
@@ -71,24 +72,9 @@ import {
|
|
|
71
72
|
* Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
|
|
72
73
|
*/
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*
|
|
77
|
-
* type-kit cuando lo publique.
|
|
78
|
-
*/
|
|
79
|
-
export interface DeviceBatchRequest<TItem> {
|
|
80
|
-
items: TItem[];
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Sobre de salida de los endpoints batch: viaja dentro de `StandardResponse.data`. Mismo gap E8. */
|
|
84
|
-
export interface DeviceBatchResult<TItem> {
|
|
85
|
-
results: TItem[];
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Sobre de salida de los endpoints de UN solo IMEI. Hoy solo `pinUnlock` no es batch. */
|
|
89
|
-
export interface DeviceSingleResult<TItem> {
|
|
90
|
-
result: TItem;
|
|
91
|
-
}
|
|
75
|
+
// Los sobres viven en `utils/MdmEnvelopes` porque los comparten todos los connectors MDM.
|
|
76
|
+
// Se re-exportan acá para no cambiar la superficie pública de `@fiado/api-invoker`.
|
|
77
|
+
export * from "../../../utils/MdmEnvelopes.js";
|
|
92
78
|
|
|
93
79
|
export interface IDatacultrConnectorApi {
|
|
94
80
|
/**
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { inject, injectable } from "inversify";
|
|
|
2
2
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
3
3
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
4
4
|
import {
|
|
5
|
+
LOAN_DEVICE_MAX_BATCH,
|
|
5
6
|
LoanDeviceEnrollRequest,
|
|
6
7
|
LoanDeviceEnrollResponse,
|
|
7
8
|
LoanDeviceEnrollStatusRequest,
|
|
@@ -12,6 +13,10 @@ import {
|
|
|
12
13
|
LoanDeviceManualLockResponse,
|
|
13
14
|
LoanDeviceManualUnlockRequest,
|
|
14
15
|
LoanDeviceManualUnlockResponse,
|
|
16
|
+
LoanDeviceNotifyRequest,
|
|
17
|
+
LoanDeviceNotifyResponse,
|
|
18
|
+
LoanDeviceReleaseRequest,
|
|
19
|
+
LoanDeviceReleaseResponse,
|
|
15
20
|
LoanDeviceStatusRequest,
|
|
16
21
|
LoanDeviceStatusResponse,
|
|
17
22
|
} from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
@@ -36,6 +41,13 @@ export default class LoanCreditDeviceApi implements ILoanCreditDeviceApi {
|
|
|
36
41
|
|
|
37
42
|
constructor(@inject("IHttpRequest") private httpRequest: IHttpRequest) {}
|
|
38
43
|
|
|
44
|
+
// Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
|
|
45
|
+
private assertBatchSize(itemCount: number): void {
|
|
46
|
+
if (itemCount > LOAN_DEVICE_MAX_BATCH) {
|
|
47
|
+
throw new Error(`El lote admite hasta ${LOAN_DEVICE_MAX_BATCH} ítems y llegaron ${itemCount}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
async enroll(
|
|
40
52
|
input: LoanDeviceEnrollRequest,
|
|
41
53
|
tenantId: string,
|
|
@@ -83,4 +95,22 @@ export default class LoanCreditDeviceApi implements ILoanCreditDeviceApi {
|
|
|
83
95
|
const url = `${this.baseUrl}/private/devices/manual-unlock?tenantId=${encodeURIComponent(tenantId)}`;
|
|
84
96
|
return await this.httpRequest.post(url, input);
|
|
85
97
|
}
|
|
98
|
+
|
|
99
|
+
async notify(
|
|
100
|
+
input: LoanDeviceNotifyRequest,
|
|
101
|
+
tenantId: string,
|
|
102
|
+
): Promise<StandardResponse<LoanDeviceNotifyResponse>> {
|
|
103
|
+
this.assertBatchSize(input.items.length);
|
|
104
|
+
const url = `${this.baseUrl}/private/devices/notify?tenantId=${encodeURIComponent(tenantId)}`;
|
|
105
|
+
return await this.httpRequest.post(url, input);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async release(
|
|
109
|
+
input: LoanDeviceReleaseRequest,
|
|
110
|
+
tenantId: string,
|
|
111
|
+
): Promise<StandardResponse<LoanDeviceReleaseResponse>> {
|
|
112
|
+
this.assertBatchSize(input.items.length);
|
|
113
|
+
const url = `${this.baseUrl}/private/devices/release?tenantId=${encodeURIComponent(tenantId)}`;
|
|
114
|
+
return await this.httpRequest.post(url, input);
|
|
115
|
+
}
|
|
86
116
|
}
|
|
@@ -10,14 +10,19 @@ import {
|
|
|
10
10
|
LoanDeviceManualLockResponse,
|
|
11
11
|
LoanDeviceManualUnlockRequest,
|
|
12
12
|
LoanDeviceManualUnlockResponse,
|
|
13
|
+
LoanDeviceNotifyRequest,
|
|
14
|
+
LoanDeviceNotifyResponse,
|
|
15
|
+
LoanDeviceReleaseRequest,
|
|
16
|
+
LoanDeviceReleaseResponse,
|
|
13
17
|
LoanDeviceStatusRequest,
|
|
14
18
|
LoanDeviceStatusResponse,
|
|
15
19
|
} from "@fiado/type-kit/bin/loanCredit/index.js";
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* Contrato del publisher de dispositivo/MDM de `loan-credit-business` (componente 09 SureKeep F2).
|
|
19
|
-
* Endpoints PRIVADOS (VPC) que envuelven al motor MDM: enrolamiento del equipo, consulta de estado
|
|
20
|
-
*
|
|
23
|
+
* Endpoints PRIVADOS (VPC) que envuelven al motor MDM: enrolamiento del equipo, consulta de estado,
|
|
24
|
+
* bloqueo/desbloqueo manual, aviso de cobranza y liberación definitiva. El contrato de crédito vive
|
|
25
|
+
* en `ILoanCreditBusinessApi`.
|
|
21
26
|
*
|
|
22
27
|
* Convenciones (idénticas a `ILoanCreditBusinessApi`):
|
|
23
28
|
* - `tenantId` OBLIGATORIO en todos los métodos — viaja por query `?tenantId=` (convención de los
|
|
@@ -26,9 +31,12 @@ import {
|
|
|
26
31
|
* - Errores de transporte (falla el lambda entero, no un ítem): el publisher RECHAZA con el
|
|
27
32
|
* `DomainError` tipado del motor — `400 UNKNOWN_TENANT` / `400` de validación del body /
|
|
28
33
|
* `5xx` del motor. Nunca fallback: el caller jamás recibe un batch vacío disfrazado de éxito.
|
|
29
|
-
* -
|
|
30
|
-
*
|
|
31
|
-
*
|
|
34
|
+
* - Techo de lote: `notify` y `release` cortan acá el lote de más de `LOAN_DEVICE_MAX_BATCH` ítems,
|
|
35
|
+
* sin salir a la red. DEUDA: los otros seis mandan el body tal cual y devuelven el 400 del
|
|
36
|
+
* destino, que valida el mismo techo — dos formas de error para la misma causa.
|
|
37
|
+
* - Idempotencia: las mutaciones (`enroll`, `manualLock`, `manualUnlock`, `notify`, `release`) son
|
|
38
|
+
* idempotentes por `input.operationReference` — es el idempotencyKey del caller. Reintentar el
|
|
39
|
+
* mismo lote con la misma referencia no repite la operación; sin ella un retry tras timeout duplica.
|
|
32
40
|
*
|
|
33
41
|
* Granularidad POR ÍTEM — el batch NUNCA falla entero:
|
|
34
42
|
* - Cada `results[i]` trae su propio `status` (`SUCCESS` | `PENDING` | `ERROR`) y un `error`
|
|
@@ -86,9 +94,41 @@ export interface ILoanCreditDeviceApi {
|
|
|
86
94
|
tenantId: string,
|
|
87
95
|
): Promise<StandardResponse<LoanDeviceManualLockResponse>>;
|
|
88
96
|
|
|
89
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* Desbloqueo manual del dispositivo disparado desde el backoffice (acción de un operador).
|
|
99
|
+
*
|
|
100
|
+
* ⚠️ El `unlockUntil` del ítem solo aplica en modo `SCHEDULED` y HOY `loan-credit-business` no
|
|
101
|
+
* lo lee: su `IDeviceControlService.manualUnlock` recibe `{ creditId }` y lo descarta en silencio.
|
|
102
|
+
*/
|
|
90
103
|
manualUnlock(
|
|
91
104
|
input: LoanDeviceManualUnlockRequest,
|
|
92
105
|
tenantId: string,
|
|
93
106
|
): Promise<StandardResponse<LoanDeviceManualUnlockResponse>>;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Manda al dispositivo el aviso de cobranza (banner o pantalla completa) sin bloquearlo.
|
|
110
|
+
*
|
|
111
|
+
* ⚠️ `loan-credit-business` TODAVÍA NO publica esta ruta: no está en su `openapi/private.yaml`
|
|
112
|
+
* ni en su `PrivateController`. Cablear un consumer antes de ese deploy devuelve 403/404.
|
|
113
|
+
*
|
|
114
|
+
* @throws Error si el lote supera `LOAN_DEVICE_MAX_BATCH` ítems (se corta acá, sin salir a la red).
|
|
115
|
+
*/
|
|
116
|
+
notify(
|
|
117
|
+
input: LoanDeviceNotifyRequest,
|
|
118
|
+
tenantId: string,
|
|
119
|
+
): Promise<StandardResponse<LoanDeviceNotifyResponse>>;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Libera el dispositivo del MDM de forma DEFINITIVA (crédito liquidado o castigado contablemente).
|
|
123
|
+
* No es un desbloqueo: saca el equipo del control del proveedor y no se revierte con `manualLock`.
|
|
124
|
+
*
|
|
125
|
+
* ⚠️ `loan-credit-business` TODAVÍA NO publica esta ruta: no está en su `openapi/private.yaml`
|
|
126
|
+
* ni en su `PrivateController`. Cablear un consumer antes de ese deploy devuelve 403/404.
|
|
127
|
+
*
|
|
128
|
+
* @throws Error si el lote supera `LOAN_DEVICE_MAX_BATCH` ítems (se corta acá, sin salir a la red).
|
|
129
|
+
*/
|
|
130
|
+
release(
|
|
131
|
+
input: LoanDeviceReleaseRequest,
|
|
132
|
+
tenantId: string,
|
|
133
|
+
): Promise<StandardResponse<LoanDeviceReleaseResponse>>;
|
|
94
134
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { inject, injectable } from "inversify";
|
|
2
2
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
3
3
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
4
|
+
import type { ValidateProductResponse } from "@fiado/type-kit/bin/retailCatalog/index.js";
|
|
4
5
|
import {
|
|
5
|
-
IMEI_PRODUCT_BATCH_MAX,
|
|
6
|
-
ImeiProductLookupResponse,
|
|
7
6
|
IRetailCatalogBusinessApi,
|
|
8
7
|
ProductsByRetailersBody,
|
|
9
8
|
ProductsByRetailersResponse,
|
|
@@ -47,18 +46,17 @@ export default class RetailCatalogBusinessApi implements IRetailCatalogBusinessA
|
|
|
47
46
|
return await this.httpRequest.get(url, undefined, this.tenantHeader(issuer));
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
async
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return await this.httpRequest.get(`${this.baseUrl}/private/products/by-imei?${query.toString()}`);
|
|
49
|
+
async validateProduct(
|
|
50
|
+
retailerId: string,
|
|
51
|
+
sku: string,
|
|
52
|
+
issuer: string,
|
|
53
|
+
): Promise<StandardResponse<ValidateProductResponse>> {
|
|
54
|
+
const path = `${encodeURIComponent(retailerId)}/${encodeURIComponent(sku)}`;
|
|
55
|
+
return await this.httpRequest.get(
|
|
56
|
+
`${this.baseUrl}/private/products/${path}`,
|
|
57
|
+
undefined,
|
|
58
|
+
this.tenantHeader(issuer),
|
|
59
|
+
);
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
async getProductsByRetailers(
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
MdmLockModeEnum,
|
|
4
|
+
MdmProviderEnum,
|
|
5
|
+
ValidateProductResponse,
|
|
6
|
+
} from "@fiado/type-kit/bin/retailCatalog/index.js";
|
|
3
7
|
|
|
4
8
|
/**
|
|
5
9
|
* Contrato del publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail)
|
|
@@ -8,15 +12,15 @@ import type { MdmLockModeEnum, MdmProviderEnum } from "@fiado/type-kit/bin/retai
|
|
|
8
12
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
9
13
|
* TENANT — DOS convenciones conviven acá. Mirá la firma de cada método.
|
|
10
14
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
11
|
-
*
|
|
15
|
+
* Todos los métodos menos `getProductsByRetailers` toman `issuer` y lo mandan en el header
|
|
12
16
|
* `x-tenant-issuer` (`PrivateController.issuerFromHeader` → `TenantContextService.withContext`).
|
|
13
|
-
* `
|
|
17
|
+
* `getProductsByRetailers` toma `tenantId` y lo manda por `?tenantId=`
|
|
14
18
|
* (→ `TenantContextService.withContextByTenantId`), porque su consumidor es un backoffice que no
|
|
15
19
|
* tiene el issuer Cognito. Los privados son `Feature.ANONIMUS` (sin RBAC; la protección es la red
|
|
16
20
|
* VPC), así que el caller DEBE declarar uno de los dos — no hay default.
|
|
17
21
|
*
|
|
18
|
-
* TD: converger los
|
|
19
|
-
*
|
|
22
|
+
* TD: converger los que van por header a `?tenantId=`, el camino canónico para service-to-service
|
|
23
|
+
* (TD-014 del destino). No se migran acá para no romper al wizard, que ya consume el header.
|
|
20
24
|
*
|
|
21
25
|
* ⚠️ Retorno `StandardResponse<T>` — el consumer lee `result.data`, NO `result.body`.
|
|
22
26
|
* Errores: el destino lanza `DomainError` tipado → el publisher RECHAZA (no devuelve null); cada
|
|
@@ -67,32 +71,6 @@ export interface StoreCatalogItem {
|
|
|
67
71
|
mdmLockMode: MdmLockModeEnum | null;
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
/** Techo de IMEIs por request del lote IMEI->producto. Lo declara y lo honra el destino. */
|
|
71
|
-
export const IMEI_PRODUCT_BATCH_MAX = 100;
|
|
72
|
-
|
|
73
|
-
/** Por que se pudo (o no) resolver el producto de un IMEI del lote. */
|
|
74
|
-
export type ImeiProductLookupStatus = "OK" | "NOT_FOUND" | "UNAVAILABLE" | "INVALID_ID";
|
|
75
|
-
|
|
76
|
-
/** Una entrada del lote IMEI->producto (espeja `ImeiProductLookupEntry` del destino). */
|
|
77
|
-
export interface ImeiProductLookupEntry {
|
|
78
|
-
imei: string;
|
|
79
|
-
/**
|
|
80
|
-
* `OK` trae sku/brand/model · `NOT_FOUND` el catalogo no llega al producto (dato POSITIVO) ·
|
|
81
|
-
* `UNAVAILABLE` no se pudo leer ese IMEI, reintentalo · `INVALID_ID` no tiene forma de IMEI.
|
|
82
|
-
*/
|
|
83
|
-
status: ImeiProductLookupStatus;
|
|
84
|
-
sku?: string;
|
|
85
|
-
/** Marca comercial (p. ej. "Apple"). Solo viene con `status: OK`. */
|
|
86
|
-
brand?: string;
|
|
87
|
-
/** Modelo (p. ej. "iPhone 15 Pro"). Solo viene con `status: OK`. */
|
|
88
|
-
model?: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Respuesta del lote: UNA entrada por IMEI pedido, en el MISMO orden. */
|
|
92
|
-
export interface ImeiProductLookupResponse {
|
|
93
|
-
items: ImeiProductLookupEntry[];
|
|
94
|
-
}
|
|
95
|
-
|
|
96
74
|
/** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
|
|
97
75
|
export const RETAILER_PRODUCTS_BATCH_MAX = 100;
|
|
98
76
|
|
|
@@ -148,29 +126,24 @@ export interface IRetailCatalogBusinessApi {
|
|
|
148
126
|
getStoreCatalog(storeId: string, issuer: string): Promise<StandardResponse<StoreCatalogItem[]>>;
|
|
149
127
|
|
|
150
128
|
/**
|
|
151
|
-
* GET `/private/products/
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
155
|
-
* ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
|
|
156
|
-
* ─────────────────────────────────────────────────────────────────────────────
|
|
157
|
-
* El consumidor es un backoffice que solo tiene el `tenantId` de su contexto; el issuer Cognito
|
|
158
|
-
* no existe de su lado. El destino entra por `TenantContextService.withContextByTenantId`, con las
|
|
159
|
-
* mismas garantías (mismo diccionario, AssumeRole del silo, fail-closed sin tenant).
|
|
129
|
+
* GET `/private/products/{retailerId}/{sku}` — valida que el producto exista y devuelve su
|
|
130
|
+
* `status`, su `tier`, sus financieras elegibles y su política MDM.
|
|
160
131
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* repetidos se consultan una sola vez pero salen repetidos. Techo de `IMEI_PRODUCT_BATCH_MAX`
|
|
164
|
-
* IMEIs por request; el caller parte los lotes mayores.
|
|
132
|
+
* ⚠️ El tenant va en el header `x-tenant-issuer`, NO por `?tenantId=` — el destino lo resuelve
|
|
133
|
+
* con `issuerFromHeader` y su `openapi/private.yaml` no declara ningún parámetro de tenant.
|
|
165
134
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* Solo un fallo total del destino rechaza la promesa.
|
|
135
|
+
* La política MDM viaja completa cuando `mdmEnabled: true`; con `false`, `mdmProvider` y
|
|
136
|
+
* `mdmLockMode` vienen en `null` (invariante `validateMdmCoherence` del catálogo).
|
|
169
137
|
*
|
|
170
|
-
* @throws
|
|
171
|
-
* `400
|
|
138
|
+
* @throws `404 PRODUCT_NOT_FOUND` el producto no existe para esa cadena ·
|
|
139
|
+
* `400 UNKNOWN_TENANT` falta o no se reconoce el issuer ·
|
|
140
|
+
* `500 TENANT_ASSUME_ROLE_FAILED` fallo de infra del silo.
|
|
172
141
|
*/
|
|
173
|
-
|
|
142
|
+
validateProduct(
|
|
143
|
+
retailerId: string,
|
|
144
|
+
sku: string,
|
|
145
|
+
issuer: string,
|
|
146
|
+
): Promise<StandardResponse<ValidateProductResponse>>;
|
|
174
147
|
|
|
175
148
|
/**
|
|
176
149
|
* POST `/private/products/by-retailers?tenantId=` — catálogo de productos de VARIAS cadenas en
|
|
@@ -179,8 +152,8 @@ export interface IRetailCatalogBusinessApi {
|
|
|
179
152
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
180
153
|
* ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
|
|
181
154
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
182
|
-
* Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`,
|
|
183
|
-
*
|
|
155
|
+
* Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, con las
|
|
156
|
+
* mismas garantías (mismo diccionario, AssumeRole del silo, fail-closed sin tenant).
|
|
184
157
|
*
|
|
185
158
|
* ── El contrato del lote ────────────────────────────────────────────────────────────────────
|
|
186
159
|
* `retailerIds` no puede venir vacío, sin repetidos y con un techo de
|