@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.
Files changed (30) hide show
  1. package/bin/datacultrConnector/api/interfaces/IDatacultrConnectorApi.d.ts +2 -16
  2. package/bin/datacultrConnector/api/interfaces/IDatacultrConnectorApi.js +49 -1
  3. package/bin/index.d.ts +1 -0
  4. package/bin/index.js +1 -0
  5. package/bin/loanCredit/api/LoanCreditDeviceApi.d.ts +4 -1
  6. package/bin/loanCredit/api/LoanCreditDeviceApi.js +17 -0
  7. package/bin/loanCredit/api/interfaces/ILoanCreditDeviceApi.d.ts +35 -7
  8. package/bin/retailCatalog/api/RetailCatalogBusinessApi.d.ts +3 -2
  9. package/bin/retailCatalog/api/RetailCatalogBusinessApi.js +4 -10
  10. package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.d.ts +17 -48
  11. package/bin/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.js +0 -2
  12. package/bin/trustonicConnector/api/TrustonicConnectorApi.d.ts +34 -0
  13. package/bin/trustonicConnector/api/TrustonicConnectorApi.js +73 -0
  14. package/bin/trustonicConnector/api/interfaces/ITrustonicConnectorApi.d.ts +158 -0
  15. package/bin/trustonicConnector/api/interfaces/ITrustonicConnectorApi.js +1 -0
  16. package/bin/trustonicConnector/index.d.ts +2 -0
  17. package/bin/trustonicConnector/index.js +2 -0
  18. package/bin/utils/MdmEnvelopes.d.ts +17 -0
  19. package/bin/utils/MdmEnvelopes.js +6 -0
  20. package/package.json +2 -2
  21. package/src/datacultrConnector/api/interfaces/IDatacultrConnectorApi.ts +4 -18
  22. package/src/index.ts +1 -0
  23. package/src/loanCredit/api/LoanCreditDeviceApi.ts +30 -0
  24. package/src/loanCredit/api/interfaces/ILoanCreditDeviceApi.ts +46 -6
  25. package/src/retailCatalog/api/RetailCatalogBusinessApi.ts +12 -14
  26. package/src/retailCatalog/api/interfaces/IRetailCatalogBusinessApi.ts +25 -52
  27. package/src/trustonicConnector/api/TrustonicConnectorApi.ts +118 -0
  28. package/src/trustonicConnector/api/interfaces/ITrustonicConnectorApi.ts +216 -0
  29. package/src/trustonicConnector/index.ts +2 -0
  30. package/src/utils/MdmEnvelopes.ts +20 -0
@@ -1,5 +1,6 @@
1
1
  import { StandardResponse } from "@fiado/gateway-adapter";
2
2
  import { DeviceAutoLockScheduleRequest, DeviceAutoLockScheduleResponse, DeviceEnrollInitiateResponse, DeviceEnrollRequest, DeviceEnrollStatusRequest, DeviceEnrollStatusResponse, DeviceLastSeenRequest, DeviceLastSeenResponse, DeviceLockRequest, DeviceLockResponse, DeviceNotifyRequest, DeviceNotifyResponse, DevicePinUnlockRequest, DevicePinUnlockResponse, DeviceReleaseInitiateResponse, DeviceReleaseRequest, DeviceReleaseStatusRequest, DeviceReleaseStatusResponse, DeviceStatusRequest, DeviceStatusResponse, DeviceUnlockRequest, DeviceUnlockResponse } from "@fiado/type-kit/bin/mdm/index.js";
3
+ import { DeviceBatchRequest, DeviceBatchResult, DeviceSingleResult } from "../../../utils/MdmEnvelopes.js";
3
4
  /**
4
5
  * Contrato del publisher HTTP del lambda `datacultr-connector` — el provider MDM que traduce el
5
6
  * contrato agnóstico de dispositivo (`@fiado/type-kit/bin/mdm`) a la API de Datacultr. Su consumidor
@@ -46,22 +47,7 @@ import { DeviceAutoLockScheduleRequest, DeviceAutoLockScheduleResponse, DeviceEn
46
47
  *
47
48
  * Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
48
49
  */
49
- /**
50
- * Sobre de entrada de los endpoints batch del connector. No vive en `@fiado/type-kit/bin/mdm`
51
- * porque los DTOs publicados son de UN imei; el sobre es dominio del líder (gap E8), migrar a
52
- * type-kit cuando lo publique.
53
- */
54
- export interface DeviceBatchRequest<TItem> {
55
- items: TItem[];
56
- }
57
- /** Sobre de salida de los endpoints batch: viaja dentro de `StandardResponse.data`. Mismo gap E8. */
58
- export interface DeviceBatchResult<TItem> {
59
- results: TItem[];
60
- }
61
- /** Sobre de salida de los endpoints de UN solo IMEI. Hoy solo `pinUnlock` no es batch. */
62
- export interface DeviceSingleResult<TItem> {
63
- result: TItem;
64
- }
50
+ export * from "../../../utils/MdmEnvelopes.js";
65
51
  export interface IDatacultrConnectorApi {
66
52
  /**
67
53
  * POST `/devices/enroll` — pre-registra la expectativa de enrolamiento de cada IMEI.
@@ -1 +1,49 @@
1
- export {};
1
+ /**
2
+ * Contrato del publisher HTTP del lambda `datacultr-connector` — el provider MDM que traduce el
3
+ * contrato agnóstico de dispositivo (`@fiado/type-kit/bin/mdm`) a la API de Datacultr. Su consumidor
4
+ * es el módulo `device/` de `loan-credit-business` (`MdmConnectorClient`), que NUNCA le pega por
5
+ * `http-client` directo: entre lambdas Fiado se habla siempre por `@fiado/api-invoker`.
6
+ *
7
+ * ─────────────────────────────────────────────────────────────────────────────
8
+ * PATHS — `/devices/...`, SIN prefijo `/private`.
9
+ * ─────────────────────────────────────────────────────────────────────────────
10
+ * A diferencia de la mayoría de los privados Fiado (`/private/...`), el `PrivateController` de este
11
+ * connector declara los paths como `/devices/enroll`, `/devices/status`, etc. El aislamiento no lo da
12
+ * el prefijo sino la red: la API privada es `EndpointConfiguration: PRIVATE` con ResourcePolicy que
13
+ * deniega todo lo que no venga de los VPC Endpoints de Fiado.
14
+ *
15
+ * ─────────────────────────────────────────────────────────────────────────────
16
+ * SIN TENANT — hoy el connector es efectivamente single-tenant.
17
+ * ─────────────────────────────────────────────────────────────────────────────
18
+ * Ningún método lleva `tenantId` porque el destino NO lo resuelve: su `PrivateController` no lee
19
+ * `X-Tenant-Id` ni `x-tenant-issuer` (cero referencias en el repo). Las credenciales de Datacultr
20
+ * salen de un único secret del lambda. DEUDA a levantar cuando el connector implemente M6
21
+ * (multi-tenant del MDM): ahí este contrato suma el tenant y pasa a ser un cambio MAJOR.
22
+ *
23
+ * ─────────────────────────────────────────────────────────────────────────────
24
+ * AUTORIZACIÓN — `Feature.ANONIMUS`.
25
+ * ─────────────────────────────────────────────────────────────────────────────
26
+ * Los privados del connector son `Feature.ANONIMUS`: sin RBAC y sin claims. La única protección es
27
+ * la VPC, así que el caller no manda `Authorization` ni headers de identidad.
28
+ *
29
+ * ⚠️ Retorno `StandardResponse<T>` — el consumer lee `result.data`, NO `result.body`.
30
+ * Errores de transporte (falla el lambda entero, no un ítem): el publisher RECHAZA con el
31
+ * `DomainError` tipado del destino. Nunca fallback: el caller jamás recibe un batch vacío
32
+ * disfrazado de éxito.
33
+ *
34
+ * Granularidad POR ÍTEM — el batch NUNCA falla entero:
35
+ * - Cada `results[i]` trae su propio `status` (`SUCCESS` | `PENDING` | `ERROR`) y un `error` opcional.
36
+ * Un 200 puede traer ítems en `ERROR`: el caller TIENE que recorrer `results` y decidir por ítem.
37
+ * - El `error.code` del ítem es un `MdmErrorCodeEnum` (`PROVIDER_NOT_SUPPORTED`, `DEVICE_NOT_FOUND`,
38
+ * `PROVIDER_TIMEOUT`, …). El `status` no lleva el motivo: el motivo vive solo en `error`.
39
+ *
40
+ * Env var requerida en el consumer: `DATACULTR_CONNECTOR_URL`.
41
+ * El template.yml del consumer la setea con:
42
+ *
43
+ * DATACULTR_CONNECTOR_URL: '{{resolve:ssm:datacultr-connector}}'
44
+ *
45
+ * Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
46
+ */
47
+ // Los sobres viven en `utils/MdmEnvelopes` porque los comparten todos los connectors MDM.
48
+ // Se re-exportan acá para no cambiar la superficie pública de `@fiado/api-invoker`.
49
+ export * from "../../../utils/MdmEnvelopes.js";
package/bin/index.d.ts CHANGED
@@ -114,3 +114,4 @@ export * from "./collection-engine/index.js";
114
114
  export * from "./domain-callback/index.js";
115
115
  export * from "./datacultrConnector/index.js";
116
116
  export * from "./webhook-business/index.js";
117
+ export * from "./trustonicConnector/index.js";
package/bin/index.js CHANGED
@@ -114,3 +114,4 @@ export * from "./collection-engine/index.js";
114
114
  export * from "./domain-callback/index.js";
115
115
  export * from "./datacultrConnector/index.js";
116
116
  export * from "./webhook-business/index.js";
117
+ export * from "./trustonicConnector/index.js";
@@ -1,6 +1,6 @@
1
1
  import type { IHttpRequest } from "@fiado/http-client";
2
2
  import { StandardResponse } from "@fiado/gateway-adapter";
3
- import { LoanDeviceEnrollRequest, LoanDeviceEnrollResponse, LoanDeviceEnrollStatusRequest, LoanDeviceEnrollStatusResponse, LoanDeviceLastSeenRequest, LoanDeviceLastSeenResponse, LoanDeviceManualLockRequest, LoanDeviceManualLockResponse, LoanDeviceManualUnlockRequest, LoanDeviceManualUnlockResponse, LoanDeviceStatusRequest, LoanDeviceStatusResponse } from "@fiado/type-kit/bin/loanCredit/index.js";
3
+ import { LoanDeviceEnrollRequest, LoanDeviceEnrollResponse, LoanDeviceEnrollStatusRequest, LoanDeviceEnrollStatusResponse, LoanDeviceLastSeenRequest, LoanDeviceLastSeenResponse, LoanDeviceManualLockRequest, LoanDeviceManualLockResponse, LoanDeviceManualUnlockRequest, LoanDeviceManualUnlockResponse, LoanDeviceNotifyRequest, LoanDeviceNotifyResponse, LoanDeviceReleaseRequest, LoanDeviceReleaseResponse, LoanDeviceStatusRequest, LoanDeviceStatusResponse } from "@fiado/type-kit/bin/loanCredit/index.js";
4
4
  import { ILoanCreditDeviceApi } from "./interfaces/ILoanCreditDeviceApi.js";
5
5
  /**
6
6
  * Publisher HTTP de los endpoints de dispositivo/MDM del lambda `loan-credit-business`
@@ -18,10 +18,13 @@ export default class LoanCreditDeviceApi implements ILoanCreditDeviceApi {
18
18
  private httpRequest;
19
19
  private readonly baseUrl;
20
20
  constructor(httpRequest: IHttpRequest);
21
+ private assertBatchSize;
21
22
  enroll(input: LoanDeviceEnrollRequest, tenantId: string): Promise<StandardResponse<LoanDeviceEnrollResponse>>;
22
23
  getEnrollStatus(input: LoanDeviceEnrollStatusRequest, tenantId: string): Promise<StandardResponse<LoanDeviceEnrollStatusResponse>>;
23
24
  getStatus(input: LoanDeviceStatusRequest, tenantId: string): Promise<StandardResponse<LoanDeviceStatusResponse>>;
24
25
  getLastSeen(input: LoanDeviceLastSeenRequest, tenantId: string): Promise<StandardResponse<LoanDeviceLastSeenResponse>>;
25
26
  manualLock(input: LoanDeviceManualLockRequest, tenantId: string): Promise<StandardResponse<LoanDeviceManualLockResponse>>;
26
27
  manualUnlock(input: LoanDeviceManualUnlockRequest, tenantId: string): Promise<StandardResponse<LoanDeviceManualUnlockResponse>>;
28
+ notify(input: LoanDeviceNotifyRequest, tenantId: string): Promise<StandardResponse<LoanDeviceNotifyResponse>>;
29
+ release(input: LoanDeviceReleaseRequest, tenantId: string): Promise<StandardResponse<LoanDeviceReleaseResponse>>;
27
30
  }
@@ -11,6 +11,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
13
  import { inject, injectable } from "inversify";
14
+ import { LOAN_DEVICE_MAX_BATCH, } from "@fiado/type-kit/bin/loanCredit/index.js";
14
15
  /**
15
16
  * Publisher HTTP de los endpoints de dispositivo/MDM del lambda `loan-credit-business`
16
17
  * (componente 09 SureKeep F2). Contrato y semántica completos → `ILoanCreditDeviceApi`
@@ -30,6 +31,12 @@ let LoanCreditDeviceApi = class LoanCreditDeviceApi {
30
31
  constructor(httpRequest) {
31
32
  this.httpRequest = httpRequest;
32
33
  }
34
+ // Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
35
+ assertBatchSize(itemCount) {
36
+ if (itemCount > LOAN_DEVICE_MAX_BATCH) {
37
+ throw new Error(`El lote admite hasta ${LOAN_DEVICE_MAX_BATCH} ítems y llegaron ${itemCount}`);
38
+ }
39
+ }
33
40
  async enroll(input, tenantId) {
34
41
  const url = `${this.baseUrl}/private/devices/enroll?tenantId=${encodeURIComponent(tenantId)}`;
35
42
  return await this.httpRequest.post(url, input);
@@ -54,6 +61,16 @@ let LoanCreditDeviceApi = class LoanCreditDeviceApi {
54
61
  const url = `${this.baseUrl}/private/devices/manual-unlock?tenantId=${encodeURIComponent(tenantId)}`;
55
62
  return await this.httpRequest.post(url, input);
56
63
  }
64
+ async notify(input, tenantId) {
65
+ this.assertBatchSize(input.items.length);
66
+ const url = `${this.baseUrl}/private/devices/notify?tenantId=${encodeURIComponent(tenantId)}`;
67
+ return await this.httpRequest.post(url, input);
68
+ }
69
+ async release(input, tenantId) {
70
+ this.assertBatchSize(input.items.length);
71
+ const url = `${this.baseUrl}/private/devices/release?tenantId=${encodeURIComponent(tenantId)}`;
72
+ return await this.httpRequest.post(url, input);
73
+ }
57
74
  };
58
75
  LoanCreditDeviceApi = __decorate([
59
76
  injectable(),
@@ -1,9 +1,10 @@
1
1
  import { StandardResponse } from "@fiado/gateway-adapter";
2
- import { LoanDeviceEnrollRequest, LoanDeviceEnrollResponse, LoanDeviceEnrollStatusRequest, LoanDeviceEnrollStatusResponse, LoanDeviceLastSeenRequest, LoanDeviceLastSeenResponse, LoanDeviceManualLockRequest, LoanDeviceManualLockResponse, LoanDeviceManualUnlockRequest, LoanDeviceManualUnlockResponse, LoanDeviceStatusRequest, LoanDeviceStatusResponse } from "@fiado/type-kit/bin/loanCredit/index.js";
2
+ import { LoanDeviceEnrollRequest, LoanDeviceEnrollResponse, LoanDeviceEnrollStatusRequest, LoanDeviceEnrollStatusResponse, LoanDeviceLastSeenRequest, LoanDeviceLastSeenResponse, LoanDeviceManualLockRequest, LoanDeviceManualLockResponse, LoanDeviceManualUnlockRequest, LoanDeviceManualUnlockResponse, LoanDeviceNotifyRequest, LoanDeviceNotifyResponse, LoanDeviceReleaseRequest, LoanDeviceReleaseResponse, LoanDeviceStatusRequest, LoanDeviceStatusResponse } from "@fiado/type-kit/bin/loanCredit/index.js";
3
3
  /**
4
4
  * Contrato del publisher de dispositivo/MDM de `loan-credit-business` (componente 09 SureKeep F2).
5
- * Endpoints PRIVADOS (VPC) que envuelven al motor MDM: enrolamiento del equipo, consulta de estado
6
- * y bloqueo/desbloqueo manual. El contrato de crédito vive en `ILoanCreditBusinessApi`.
5
+ * Endpoints PRIVADOS (VPC) que envuelven al motor MDM: enrolamiento del equipo, consulta de estado,
6
+ * bloqueo/desbloqueo manual, aviso de cobranza y liberación definitiva. El contrato de crédito vive
7
+ * en `ILoanCreditBusinessApi`.
7
8
  *
8
9
  * Convenciones (idénticas a `ILoanCreditBusinessApi`):
9
10
  * - `tenantId` OBLIGATORIO en todos los métodos — viaja por query `?tenantId=` (convención de los
@@ -12,9 +13,12 @@ import { LoanDeviceEnrollRequest, LoanDeviceEnrollResponse, LoanDeviceEnrollStat
12
13
  * - Errores de transporte (falla el lambda entero, no un ítem): el publisher RECHAZA con el
13
14
  * `DomainError` tipado del motor — `400 UNKNOWN_TENANT` / `400` de validación del body /
14
15
  * `5xx` del motor. Nunca fallback: el caller jamás recibe un batch vacío disfrazado de éxito.
15
- * - Idempotencia: las 3 mutaciones (`enroll`, `manualLock`, `manualUnlock`) son idempotentes por
16
- * `input.operationReference` — es el idempotencyKey del caller. Reintentar el mismo lote con la
17
- * misma referencia no vuelve a enrolar ni a bloquear; sin ella un retry tras timeout duplica.
16
+ * - Techo de lote: `notify` y `release` cortan acá el lote de más de `LOAN_DEVICE_MAX_BATCH` ítems,
17
+ * sin salir a la red. DEUDA: los otros seis mandan el body tal cual y devuelven el 400 del
18
+ * destino, que valida el mismo techo — dos formas de error para la misma causa.
19
+ * - Idempotencia: las mutaciones (`enroll`, `manualLock`, `manualUnlock`, `notify`, `release`) son
20
+ * idempotentes por `input.operationReference` — es el idempotencyKey del caller. Reintentar el
21
+ * mismo lote con la misma referencia no repite la operación; sin ella un retry tras timeout duplica.
18
22
  *
19
23
  * Granularidad POR ÍTEM — el batch NUNCA falla entero:
20
24
  * - Cada `results[i]` trae su propio `status` (`SUCCESS` | `PENDING` | `ERROR`) y un `error`
@@ -52,6 +56,30 @@ export interface ILoanCreditDeviceApi {
52
56
  getLastSeen(input: LoanDeviceLastSeenRequest, tenantId: string): Promise<StandardResponse<LoanDeviceLastSeenResponse>>;
53
57
  /** Bloqueo manual del dispositivo disparado desde el backoffice (acción de un operador). */
54
58
  manualLock(input: LoanDeviceManualLockRequest, tenantId: string): Promise<StandardResponse<LoanDeviceManualLockResponse>>;
55
- /** Desbloqueo manual del dispositivo disparado desde el backoffice (acción de un operador). */
59
+ /**
60
+ * Desbloqueo manual del dispositivo disparado desde el backoffice (acción de un operador).
61
+ *
62
+ * ⚠️ El `unlockUntil` del ítem solo aplica en modo `SCHEDULED` y HOY `loan-credit-business` no
63
+ * lo lee: su `IDeviceControlService.manualUnlock` recibe `{ creditId }` y lo descarta en silencio.
64
+ */
56
65
  manualUnlock(input: LoanDeviceManualUnlockRequest, tenantId: string): Promise<StandardResponse<LoanDeviceManualUnlockResponse>>;
66
+ /**
67
+ * Manda al dispositivo el aviso de cobranza (banner o pantalla completa) sin bloquearlo.
68
+ *
69
+ * ⚠️ `loan-credit-business` TODAVÍA NO publica esta ruta: no está en su `openapi/private.yaml`
70
+ * ni en su `PrivateController`. Cablear un consumer antes de ese deploy devuelve 403/404.
71
+ *
72
+ * @throws Error si el lote supera `LOAN_DEVICE_MAX_BATCH` ítems (se corta acá, sin salir a la red).
73
+ */
74
+ notify(input: LoanDeviceNotifyRequest, tenantId: string): Promise<StandardResponse<LoanDeviceNotifyResponse>>;
75
+ /**
76
+ * Libera el dispositivo del MDM de forma DEFINITIVA (crédito liquidado o castigado contablemente).
77
+ * No es un desbloqueo: saca el equipo del control del proveedor y no se revierte con `manualLock`.
78
+ *
79
+ * ⚠️ `loan-credit-business` TODAVÍA NO publica esta ruta: no está en su `openapi/private.yaml`
80
+ * ni en su `PrivateController`. Cablear un consumer antes de ese deploy devuelve 403/404.
81
+ *
82
+ * @throws Error si el lote supera `LOAN_DEVICE_MAX_BATCH` ítems (se corta acá, sin salir a la red).
83
+ */
84
+ release(input: LoanDeviceReleaseRequest, tenantId: string): Promise<StandardResponse<LoanDeviceReleaseResponse>>;
57
85
  }
@@ -1,6 +1,7 @@
1
1
  import type { IHttpRequest } from "@fiado/http-client";
2
2
  import { StandardResponse } from "@fiado/gateway-adapter";
3
- import { ImeiProductLookupResponse, IRetailCatalogBusinessApi, ProductsByRetailersResponse, ReleaseReservationBody, ReserveInventoryBody, ReserveInventoryResult, SellInventoryBody, StoreCatalogItem } from "./interfaces/IRetailCatalogBusinessApi.js";
3
+ import type { ValidateProductResponse } from "@fiado/type-kit/bin/retailCatalog/index.js";
4
+ import { IRetailCatalogBusinessApi, ProductsByRetailersResponse, ReleaseReservationBody, ReserveInventoryBody, ReserveInventoryResult, SellInventoryBody, StoreCatalogItem } from "./interfaces/IRetailCatalogBusinessApi.js";
4
5
  /**
5
6
  * Publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail) para sus
6
7
  * endpoints privados del ciclo de venta del IMEI. Contrato y semántica completos →
@@ -20,7 +21,7 @@ export default class RetailCatalogBusinessApi implements IRetailCatalogBusinessA
20
21
  constructor(httpRequest: IHttpRequest);
21
22
  private tenantHeader;
22
23
  getStoreCatalog(storeId: string, issuer: string): Promise<StandardResponse<StoreCatalogItem[]>>;
23
- getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
24
+ validateProduct(retailerId: string, sku: string, issuer: string): Promise<StandardResponse<ValidateProductResponse>>;
24
25
  getProductsByRetailers(retailerIds: string[], tenantId: string): Promise<StandardResponse<ProductsByRetailersResponse>>;
25
26
  reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
26
27
  sellInventory(imei: string, body: SellInventoryBody, issuer: string): Promise<StandardResponse<void>>;
@@ -11,7 +11,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
11
  return function (target, key) { decorator(target, key, paramIndex); }
12
12
  };
13
13
  import { inject, injectable } from "inversify";
14
- import { IMEI_PRODUCT_BATCH_MAX, RETAILER_PRODUCTS_BATCH_MAX, } from "./interfaces/IRetailCatalogBusinessApi.js";
14
+ import { RETAILER_PRODUCTS_BATCH_MAX, } from "./interfaces/IRetailCatalogBusinessApi.js";
15
15
  /** Header por el que el destino resuelve el silo (issuer Cognito forwardeado por el caller). */
16
16
  const TENANT_ISSUER_HEADER = "x-tenant-issuer";
17
17
  /**
@@ -41,15 +41,9 @@ let RetailCatalogBusinessApi = class RetailCatalogBusinessApi {
41
41
  const url = `${this.baseUrl}/private/stores/${encodeURIComponent(storeId)}/catalog`;
42
42
  return await this.httpRequest.get(url, undefined, this.tenantHeader(issuer));
43
43
  }
44
- async getProductsByImei(imeis, tenantId) {
45
- // Se corta acá: mandar un lote que el destino va a rechazar solo gasta un round-trip.
46
- if (imeis.length > IMEI_PRODUCT_BATCH_MAX) {
47
- throw new Error(`El lote admite hasta ${IMEI_PRODUCT_BATCH_MAX} IMEIs y llegaron ${imeis.length}`);
48
- }
49
- // `URLSearchParams` codifica la coma del separador; el destino hace `split(',')` sobre el
50
- // valor ya decodificado por API Gateway.
51
- const query = new URLSearchParams({ imeis: imeis.join(","), tenantId });
52
- return await this.httpRequest.get(`${this.baseUrl}/private/products/by-imei?${query.toString()}`);
44
+ async validateProduct(retailerId, sku, issuer) {
45
+ const path = `${encodeURIComponent(retailerId)}/${encodeURIComponent(sku)}`;
46
+ return await this.httpRequest.get(`${this.baseUrl}/private/products/${path}`, undefined, this.tenantHeader(issuer));
53
47
  }
54
48
  async getProductsByRetailers(retailerIds, tenantId) {
55
49
  // Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
@@ -1,5 +1,5 @@
1
1
  import { StandardResponse } from "@fiado/gateway-adapter";
2
- import type { MdmLockModeEnum, MdmProviderEnum } from "@fiado/type-kit/bin/retailCatalog/index.js";
2
+ import type { MdmLockModeEnum, MdmProviderEnum, ValidateProductResponse } from "@fiado/type-kit/bin/retailCatalog/index.js";
3
3
  /**
4
4
  * Contrato del publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail)
5
5
  * para sus endpoints privados del ciclo de venta del IMEI, consumidos por el wizard F2.
@@ -7,15 +7,15 @@ import type { MdmLockModeEnum, MdmProviderEnum } from "@fiado/type-kit/bin/retai
7
7
  * ─────────────────────────────────────────────────────────────────────────────
8
8
  * TENANT — DOS convenciones conviven acá. Mirá la firma de cada método.
9
9
  * ─────────────────────────────────────────────────────────────────────────────
10
- * Los cinco métodos del ciclo de venta del IMEI toman `issuer` y lo mandan en el header
10
+ * Todos los métodos menos `getProductsByRetailers` toman `issuer` y lo mandan en el header
11
11
  * `x-tenant-issuer` (`PrivateController.issuerFromHeader` → `TenantContextService.withContext`).
12
- * `getProductsByImei` toma `tenantId` y lo manda por `?tenantId=`
12
+ * `getProductsByRetailers` toma `tenantId` y lo manda por `?tenantId=`
13
13
  * (→ `TenantContextService.withContextByTenantId`), porque su consumidor es un backoffice que no
14
14
  * tiene el issuer Cognito. Los privados son `Feature.ANONIMUS` (sin RBAC; la protección es la red
15
15
  * VPC), así que el caller DEBE declarar uno de los dos — no hay default.
16
16
  *
17
- * TD: converger los cinco del ciclo de venta a `?tenantId=`, el camino canónico para
18
- * service-to-service. No se migran acá para no romper al wizard, que ya consume el header.
17
+ * TD: converger los que van por header a `?tenantId=`, el camino canónico para service-to-service
18
+ * (TD-014 del destino). No se migran acá para no romper al wizard, que ya consume el header.
19
19
  *
20
20
  * ⚠️ Retorno `StandardResponse<T>` — el consumer lee `result.data`, NO `result.body`.
21
21
  * Errores: el destino lanza `DomainError` tipado → el publisher RECHAZA (no devuelve null); cada
@@ -58,28 +58,6 @@ export interface StoreCatalogItem {
58
58
  /** Modo de bloqueo pactado; `null` si el producto no lleva MDM. */
59
59
  mdmLockMode: MdmLockModeEnum | null;
60
60
  }
61
- /** Techo de IMEIs por request del lote IMEI->producto. Lo declara y lo honra el destino. */
62
- export declare const IMEI_PRODUCT_BATCH_MAX = 100;
63
- /** Por que se pudo (o no) resolver el producto de un IMEI del lote. */
64
- export type ImeiProductLookupStatus = "OK" | "NOT_FOUND" | "UNAVAILABLE" | "INVALID_ID";
65
- /** Una entrada del lote IMEI->producto (espeja `ImeiProductLookupEntry` del destino). */
66
- export interface ImeiProductLookupEntry {
67
- imei: string;
68
- /**
69
- * `OK` trae sku/brand/model · `NOT_FOUND` el catalogo no llega al producto (dato POSITIVO) ·
70
- * `UNAVAILABLE` no se pudo leer ese IMEI, reintentalo · `INVALID_ID` no tiene forma de IMEI.
71
- */
72
- status: ImeiProductLookupStatus;
73
- sku?: string;
74
- /** Marca comercial (p. ej. "Apple"). Solo viene con `status: OK`. */
75
- brand?: string;
76
- /** Modelo (p. ej. "iPhone 15 Pro"). Solo viene con `status: OK`. */
77
- model?: string;
78
- }
79
- /** Respuesta del lote: UNA entrada por IMEI pedido, en el MISMO orden. */
80
- export interface ImeiProductLookupResponse {
81
- items: ImeiProductLookupEntry[];
82
- }
83
61
  /** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
84
62
  export declare const RETAILER_PRODUCTS_BATCH_MAX = 100;
85
63
  /** Body de POST `/private/products/by-retailers`. */
@@ -126,29 +104,20 @@ export interface IRetailCatalogBusinessApi {
126
104
  /** GET `/private/stores/{storeId}/catalog` — catálogo vendible (sku, imei, precio de contado, disponibilidad). */
127
105
  getStoreCatalog(storeId: string, issuer: string): Promise<StandardResponse<StoreCatalogItem[]>>;
128
106
  /**
129
- * GET `/private/products/by-imei?imeis=&tenantId=` — resuelve un lote de IMEIs a su producto
130
- * (sku + marca + modelo). Para los lambdas que del equipo solo guardan el IMEI.
131
- *
132
- * ─────────────────────────────────────────────────────────────────────────────
133
- * ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
134
- * ─────────────────────────────────────────────────────────────────────────────
135
- * El consumidor es un backoffice que solo tiene el `tenantId` de su contexto; el issuer Cognito
136
- * no existe de su lado. El destino entra por `TenantContextService.withContextByTenantId`, con las
137
- * mismas garantías (mismo diccionario, AssumeRole del silo, fail-closed sin tenant).
107
+ * GET `/private/products/{retailerId}/{sku}` — valida que el producto exista y devuelve su
108
+ * `status`, su `tier`, sus financieras elegibles y su política MDM.
138
109
  *
139
- * ── El contrato del lote ────────────────────────────────────────────────────────────────────
140
- * Devuelve UNA entrada por cada IMEI pedido, en el MISMO orden — el caller nunca reconcilia. Los
141
- * repetidos se consultan una sola vez pero salen repetidos. Techo de `IMEI_PRODUCT_BATCH_MAX`
142
- * IMEIs por request; el caller parte los lotes mayores.
110
+ * ⚠️ El tenant va en el header `x-tenant-issuer`, NO por `?tenantId=` — el destino lo resuelve
111
+ * con `issuerFromHeader` y su `openapi/private.yaml` no declara ningún parámetro de tenant.
143
112
  *
144
- * Ningún IMEI tumba el lote: el mal formado sale `INVALID_ID`, el que el catálogo no alcanza a
145
- * resolver sale `NOT_FOUND` y el que DynamoDB no llegó a leer sale `UNAVAILABLE` (reintentable).
146
- * Solo un fallo total del destino rechaza la promesa.
113
+ * La política MDM viaja completa cuando `mdmEnabled: true`; con `false`, `mdmProvider` y
114
+ * `mdmLockMode` vienen en `null` (invariante `validateMdmCoherence` del catálogo).
147
115
  *
148
- * @throws Error si el lote supera el techo (se corta acá, sin salir a la red) ·
149
- * `400 IMEI_BATCH_REQUIRED` si `imeis` viene vacío · `400 UNKNOWN_TENANT` sin `tenantId`.
116
+ * @throws `404 PRODUCT_NOT_FOUND` el producto no existe para esa cadena ·
117
+ * `400 UNKNOWN_TENANT` falta o no se reconoce el issuer ·
118
+ * `500 TENANT_ASSUME_ROLE_FAILED` fallo de infra del silo.
150
119
  */
151
- getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
120
+ validateProduct(retailerId: string, sku: string, issuer: string): Promise<StandardResponse<ValidateProductResponse>>;
152
121
  /**
153
122
  * POST `/private/products/by-retailers?tenantId=` — catálogo de productos de VARIAS cadenas en
154
123
  * una sola llamada, para el consumidor que arma su vista con N cadenas y no quiere N requests.
@@ -156,8 +125,8 @@ export interface IRetailCatalogBusinessApi {
156
125
  * ─────────────────────────────────────────────────────────────────────────────
157
126
  * ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
158
127
  * ─────────────────────────────────────────────────────────────────────────────
159
- * Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, igual que
160
- * `getProductsByImei`.
128
+ * Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, con las
129
+ * mismas garantías (mismo diccionario, AssumeRole del silo, fail-closed sin tenant).
161
130
  *
162
131
  * ── El contrato del lote ────────────────────────────────────────────────────────────────────
163
132
  * `retailerIds` no puede venir vacío, sin repetidos y con un techo de
@@ -1,4 +1,2 @@
1
- /** Techo de IMEIs por request del lote IMEI->producto. Lo declara y lo honra el destino. */
2
- export const IMEI_PRODUCT_BATCH_MAX = 100;
3
1
  /** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
4
2
  export const RETAILER_PRODUCTS_BATCH_MAX = 100;
@@ -0,0 +1,34 @@
1
+ import type { IHttpRequest } from "@fiado/http-client";
2
+ import { StandardResponse } from "@fiado/gateway-adapter";
3
+ import { DeviceAutoLockScheduleRequest, DeviceAutoLockScheduleResponse, DeviceEnrollInitiateResponse, DeviceEnrollRequest, DeviceEnrollStatusRequest, DeviceEnrollStatusResponse, DeviceLastSeenRequest, DeviceLastSeenResponse, DeviceLockRequest, DeviceLockResponse, DeviceNotifyRequest, DeviceNotifyResponse, DevicePinUnlockRequest, DevicePinUnlockResponse, DeviceReleaseInitiateResponse, DeviceReleaseRequest, DeviceReleaseStatusRequest, DeviceReleaseStatusResponse, DeviceStatusRequest, DeviceStatusResponse, DeviceUnlockRequest, DeviceUnlockResponse } from "@fiado/type-kit/bin/mdm/index.js";
4
+ import { DeviceBatchRequest, DeviceBatchResult } from "../../utils/MdmEnvelopes.js";
5
+ import { DeviceInitiateBatchResult, DevicePollingBatchRequest, ITrustonicConnectorApi } from "./interfaces/ITrustonicConnectorApi.js";
6
+ /**
7
+ * Publisher HTTP del lambda `trustonic-connector` (segundo provider MDM de SureKeep). Contrato y
8
+ * semántica completos → `ITrustonicConnectorApi` (paths sin prefijo `/private`, sin tenant,
9
+ * `StandardResponse` → leer `result.data`, granularidad por ítem, y las cinco diferencias reales
10
+ * contra el connector de Datacultr).
11
+ *
12
+ * Env var requerida en el consumer: `TRUSTONIC_CONNECTOR_URL`.
13
+ * El template.yml del consumer la setea con:
14
+ *
15
+ * TRUSTONIC_CONNECTOR_URL: '{{resolve:ssm:trustonic-connector}}'
16
+ *
17
+ * Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
18
+ */
19
+ export default class TrustonicConnectorApi implements ITrustonicConnectorApi {
20
+ private httpRequest;
21
+ private readonly baseUrl;
22
+ constructor(httpRequest: IHttpRequest);
23
+ enrollDevices(input: DeviceBatchRequest<DeviceEnrollRequest>): Promise<StandardResponse<DeviceInitiateBatchResult<DeviceEnrollInitiateResponse>>>;
24
+ enrollStatus(input: DevicePollingBatchRequest<DeviceEnrollStatusRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceEnrollStatusResponse>>>;
25
+ getStatus(input: DeviceBatchRequest<DeviceStatusRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceStatusResponse>>>;
26
+ getLastSeen(input: DeviceBatchRequest<DeviceLastSeenRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceLastSeenResponse>>>;
27
+ lockDevices(input: DeviceBatchRequest<DeviceLockRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceLockResponse>>>;
28
+ unlockDevices(input: DeviceBatchRequest<DeviceUnlockRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceUnlockResponse>>>;
29
+ autoLockSchedule(input: DeviceBatchRequest<DeviceAutoLockScheduleRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceAutoLockScheduleResponse>>>;
30
+ pinUnlock(input: DevicePinUnlockRequest): Promise<StandardResponse<DevicePinUnlockResponse>>;
31
+ notifyDevices(input: DeviceBatchRequest<DeviceNotifyRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceNotifyResponse>>>;
32
+ releaseDevices(input: DeviceBatchRequest<DeviceReleaseRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceReleaseInitiateResponse>>>;
33
+ releaseStatus(input: DevicePollingBatchRequest<DeviceReleaseStatusRequest>): Promise<StandardResponse<DeviceBatchResult<DeviceReleaseStatusResponse>>>;
34
+ }
@@ -0,0 +1,73 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { inject, injectable } from "inversify";
14
+ /**
15
+ * Publisher HTTP del lambda `trustonic-connector` (segundo provider MDM de SureKeep). Contrato y
16
+ * semántica completos → `ITrustonicConnectorApi` (paths sin prefijo `/private`, sin tenant,
17
+ * `StandardResponse` → leer `result.data`, granularidad por ítem, y las cinco diferencias reales
18
+ * contra el connector de Datacultr).
19
+ *
20
+ * Env var requerida en el consumer: `TRUSTONIC_CONNECTOR_URL`.
21
+ * El template.yml del consumer la setea con:
22
+ *
23
+ * TRUSTONIC_CONNECTOR_URL: '{{resolve:ssm:trustonic-connector}}'
24
+ *
25
+ * Convención CLAUDE.md global: SSM key = nombre del lambda owner de la URL.
26
+ */
27
+ let TrustonicConnectorApi = class TrustonicConnectorApi {
28
+ httpRequest;
29
+ // El buildspec publica la URL en SSM con "/" final — se normaliza para no armar "//devices".
30
+ baseUrl = (process.env.TRUSTONIC_CONNECTOR_URL || "").replace(/\/+$/, "");
31
+ constructor(httpRequest) {
32
+ this.httpRequest = httpRequest;
33
+ }
34
+ async enrollDevices(input) {
35
+ return await this.httpRequest.post(`${this.baseUrl}/devices/enroll`, input);
36
+ }
37
+ async enrollStatus(input) {
38
+ return await this.httpRequest.post(`${this.baseUrl}/devices/enroll/status`, input);
39
+ }
40
+ async getStatus(input) {
41
+ return await this.httpRequest.post(`${this.baseUrl}/devices/status`, input);
42
+ }
43
+ async getLastSeen(input) {
44
+ return await this.httpRequest.post(`${this.baseUrl}/devices/last-seen`, input);
45
+ }
46
+ async lockDevices(input) {
47
+ return await this.httpRequest.post(`${this.baseUrl}/devices/lock`, input);
48
+ }
49
+ async unlockDevices(input) {
50
+ return await this.httpRequest.post(`${this.baseUrl}/devices/unlock`, input);
51
+ }
52
+ async autoLockSchedule(input) {
53
+ return await this.httpRequest.post(`${this.baseUrl}/devices/auto-lock-schedule`, input);
54
+ }
55
+ async pinUnlock(input) {
56
+ return await this.httpRequest.post(`${this.baseUrl}/devices/pin-unlock`, input);
57
+ }
58
+ async notifyDevices(input) {
59
+ return await this.httpRequest.post(`${this.baseUrl}/devices/notify`, input);
60
+ }
61
+ async releaseDevices(input) {
62
+ return await this.httpRequest.post(`${this.baseUrl}/devices/release`, input);
63
+ }
64
+ async releaseStatus(input) {
65
+ return await this.httpRequest.post(`${this.baseUrl}/devices/release/status`, input);
66
+ }
67
+ };
68
+ TrustonicConnectorApi = __decorate([
69
+ injectable(),
70
+ __param(0, inject("IHttpRequest")),
71
+ __metadata("design:paramtypes", [Object])
72
+ ], TrustonicConnectorApi);
73
+ export default TrustonicConnectorApi;