@fiado/api-invoker 5.60.0 → 5.62.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.
@@ -23,7 +23,7 @@ export default class LoanOfferingsBusinessApi implements ILoanOfferingsBusinessA
23
23
  listActiveFinanciers(tenantId: string): Promise<StandardResponse<FinancierResponse[]>>;
24
24
  simulate(planId: string, input: SimulateCreditPlanRequest, tenantId: string): Promise<StandardResponse<SimulationResultResponse>>;
25
25
  consumePromotion(promotionId: string, input: ConsumePromotionRequest, tenantId: string): Promise<StandardResponse<ConsumePromotionResponse>>;
26
- listPromotedSkus(storeId: string, tenantId: string): Promise<StandardResponse<PromotedSkusResponse>>;
26
+ listPromotedSkus(storeId: string, tenantId: string, level?: CreditPlanLevelEnum, segmentId?: string): Promise<StandardResponse<PromotedSkusResponse>>;
27
27
  resolveCustomerSegments(input: ResolveCustomerSegmentsRequest, tenantId: string): Promise<StandardResponse<ResolveCustomerSegmentsResponse>>;
28
28
  reservePromotionBonus(promotionId: string, input: ReservePromotionBonusRequest, tenantId: string): Promise<StandardResponse<ReservePromotionBonusResponse>>;
29
29
  settlePromotionBonus(promotionId: string, input: SettlePromotionBonusRequest, tenantId: string): Promise<StandardResponse<PromotionBonusResponse>>;
@@ -50,8 +50,12 @@ let LoanOfferingsBusinessApi = class LoanOfferingsBusinessApi {
50
50
  const url = `${this.baseUrl}/private/promotions/${encodeURIComponent(promotionId)}/consume?tenantId=${encodeURIComponent(tenantId)}`;
51
51
  return await this.httpRequest.post(url, input);
52
52
  }
53
- async listPromotedSkus(storeId, tenantId) {
54
- const url = `${this.baseUrl}/private/promotions/promoted-skus?storeId=${encodeURIComponent(storeId)}&tenantId=${encodeURIComponent(tenantId)}`;
53
+ async listPromotedSkus(storeId, tenantId, level, segmentId) {
54
+ let url = `${this.baseUrl}/private/promotions/promoted-skus?storeId=${encodeURIComponent(storeId)}&tenantId=${encodeURIComponent(tenantId)}`;
55
+ if (level)
56
+ url += `&level=${encodeURIComponent(level)}`;
57
+ if (segmentId)
58
+ url += `&segmentId=${encodeURIComponent(segmentId)}`;
55
59
  return await this.httpRequest.get(url);
56
60
  }
57
61
  async resolveCustomerSegments(input, tenantId) {
@@ -81,8 +81,11 @@ export interface ILoanOfferingsBusinessApi {
81
81
  * El filtrado vive en el lambda, no acá: solo entran las promociones que alcanzan a CUALQUIER
82
82
  * nivel (el catálogo se pinta antes del score) y las que ACOTAN por SKU (una general marcaría
83
83
  * el catálogo entero). El caller no puede aflojar ese criterio.
84
+ *
85
+ * `level`/`segmentId` son opcionales, mismo criterio que {@link listSkuPromotions}: si no
86
+ * vienen, el lambda destino descarta las promociones que acotan por esa dimensión.
84
87
  */
85
- listPromotedSkus(storeId: string, tenantId: string): Promise<StandardResponse<PromotedSkusResponse>>;
88
+ listPromotedSkus(storeId: string, tenantId: string, level?: CreditPlanLevelEnum, segmentId?: string): Promise<StandardResponse<PromotedSkusResponse>>;
86
89
  /**
87
90
  * POST /private/segments/resolve — los segmentos a los que pertenece el cliente HOY, evaluados
88
91
  * en línea contra los hechos que manda el caller. No hay segmento persistido en el cliente.
@@ -1,6 +1,6 @@
1
1
  import type { IHttpRequest } from "@fiado/http-client";
2
2
  import { StandardResponse } from "@fiado/gateway-adapter";
3
- import { ImeiProductLookupResponse, IRetailCatalogBusinessApi, ReleaseReservationBody, ReserveInventoryBody, ReserveInventoryResult, SellInventoryBody, StoreCatalogItem } from "./interfaces/IRetailCatalogBusinessApi.js";
3
+ import { ImeiProductLookupResponse, IRetailCatalogBusinessApi, ProductsByRetailersResponse, ReleaseReservationBody, ReserveInventoryBody, ReserveInventoryResult, SellInventoryBody, StoreCatalogItem } from "./interfaces/IRetailCatalogBusinessApi.js";
4
4
  /**
5
5
  * Publisher HTTP del lambda `retail-catalog-business` (SureKeep Fase 1, pista Retail) para sus
6
6
  * endpoints privados del ciclo de venta del IMEI. Contrato y semántica completos →
@@ -21,6 +21,7 @@ export default class RetailCatalogBusinessApi implements IRetailCatalogBusinessA
21
21
  private tenantHeader;
22
22
  getStoreCatalog(storeId: string, issuer: string): Promise<StandardResponse<StoreCatalogItem[]>>;
23
23
  getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
24
+ getProductsByRetailers(retailerIds: string[], tenantId: string): Promise<StandardResponse<ProductsByRetailersResponse>>;
24
25
  reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
25
26
  sellInventory(imei: string, body: SellInventoryBody, issuer: string): Promise<StandardResponse<void>>;
26
27
  releaseInventory(imei: string, body: ReleaseReservationBody, 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, } from "./interfaces/IRetailCatalogBusinessApi.js";
14
+ import { IMEI_PRODUCT_BATCH_MAX, 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
  /**
@@ -51,6 +51,15 @@ let RetailCatalogBusinessApi = class RetailCatalogBusinessApi {
51
51
  const query = new URLSearchParams({ imeis: imeis.join(","), tenantId });
52
52
  return await this.httpRequest.get(`${this.baseUrl}/private/products/by-imei?${query.toString()}`);
53
53
  }
54
+ async getProductsByRetailers(retailerIds, tenantId) {
55
+ // Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
56
+ if (retailerIds.length > RETAILER_PRODUCTS_BATCH_MAX) {
57
+ throw new Error(`El lote admite hasta ${RETAILER_PRODUCTS_BATCH_MAX} cadenas y llegaron ${retailerIds.length}`);
58
+ }
59
+ const query = new URLSearchParams({ tenantId });
60
+ const body = { retailerIds };
61
+ return await this.httpRequest.post(`${this.baseUrl}/private/products/by-retailers?${query.toString()}`, body);
62
+ }
54
63
  async reserveInventory(imei, body, issuer) {
55
64
  const url = `${this.baseUrl}/private/inventory/${encodeURIComponent(imei)}/reserve`;
56
65
  return await this.httpRequest.put(url, body, this.tenantHeader(issuer));
@@ -80,6 +80,30 @@ export interface ImeiProductLookupEntry {
80
80
  export interface ImeiProductLookupResponse {
81
81
  items: ImeiProductLookupEntry[];
82
82
  }
83
+ /** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
84
+ export declare const RETAILER_PRODUCTS_BATCH_MAX = 100;
85
+ /** Body de POST `/private/products/by-retailers`. */
86
+ export interface ProductsByRetailersBody {
87
+ retailerIds: string[];
88
+ }
89
+ /** Un producto vendible de una cadena (espeja `RetailerProductItem` del destino). */
90
+ export interface RetailerProductItem {
91
+ /** Cadena a la que pertenece el producto; es la llave con la que el caller agrupa. */
92
+ retailerId: string;
93
+ sku: string;
94
+ /** Marca comercial (p. ej. "Samsung"). */
95
+ brand: string;
96
+ /** Modelo (p. ej. "A54"). */
97
+ model: string;
98
+ }
99
+ /** Respuesta del lote cadena->catálogo: los productos de TODAS las cadenas pedidas, en una lista plana. */
100
+ export interface ProductsByRetailersResponse {
101
+ items: RetailerProductItem[];
102
+ /** Cuántos productos trae `items`. */
103
+ count: number;
104
+ /** `true` si el destino recortó el resultado; pide las cadenas en lotes más chicos. */
105
+ truncated: boolean;
106
+ }
83
107
  /** Resultado de la reserva (espeja `ReserveInventoryResponse` del destino). */
84
108
  export interface ReserveInventoryResult {
85
109
  reservationExpiresAt: string;
@@ -125,6 +149,32 @@ export interface IRetailCatalogBusinessApi {
125
149
  * `400 IMEI_BATCH_REQUIRED` si `imeis` viene vacío · `400 UNKNOWN_TENANT` sin `tenantId`.
126
150
  */
127
151
  getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
152
+ /**
153
+ * POST `/private/products/by-retailers?tenantId=` — catálogo de productos de VARIAS cadenas en
154
+ * una sola llamada, para el consumidor que arma su vista con N cadenas y no quiere N requests.
155
+ *
156
+ * ─────────────────────────────────────────────────────────────────────────────
157
+ * ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
158
+ * ─────────────────────────────────────────────────────────────────────────────
159
+ * Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, igual que
160
+ * `getProductsByImei`.
161
+ *
162
+ * ── El contrato del lote ────────────────────────────────────────────────────────────────────
163
+ * `retailerIds` no puede venir vacío, sin repetidos y con un techo de
164
+ * `RETAILER_PRODUCTS_BATCH_MAX` cadenas; el caller parte los lotes mayores. La respuesta es una
165
+ * lista PLANA de productos con su `retailerId` — el caller agrupa. Una cadena sin catálogo
166
+ * simplemente no aporta ítems.
167
+ *
168
+ * O el lote sale completo o no sale: si falla la lectura de al menos una cadena el destino
169
+ * responde `INTERNAL_ERROR`. Nunca recibes una lista parcial.
170
+ *
171
+ * @throws Error si el lote supera el techo (se corta aquí, sin salir a la red) ·
172
+ * `400 VALIDATION_ERROR` body vacío, con repetidos, con no-strings o de más de 100 ids ·
173
+ * `400 UNKNOWN_TENANT` falta o no se reconoce el `tenantId` ·
174
+ * `500 TENANT_ASSUME_ROLE_FAILED` fallo de infra del silo ·
175
+ * `500 INTERNAL_ERROR` no se pudo leer alguna cadena.
176
+ */
177
+ getProductsByRetailers(retailerIds: string[], tenantId: string): Promise<StandardResponse<ProductsByRetailersResponse>>;
128
178
  /** PUT `/private/inventory/{imei}/reserve` — reserva atómica del IMEI para una sesión (AVAILABLE → RESERVED). */
129
179
  reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
130
180
  /** PUT `/private/inventory/{imei}/sell` — vende el IMEI (RESERVED → SOLD). */
@@ -1,2 +1,4 @@
1
1
  /** Techo de IMEIs por request del lote IMEI->producto. Lo declara y lo honra el destino. */
2
2
  export const IMEI_PRODUCT_BATCH_MAX = 100;
3
+ /** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
4
+ export const RETAILER_PRODUCTS_BATCH_MAX = 100;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/api-invoker",
3
- "version": "5.60.0",
3
+ "version": "5.62.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",
@@ -84,8 +84,12 @@ export default class LoanOfferingsBusinessApi implements ILoanOfferingsBusinessA
84
84
  async listPromotedSkus(
85
85
  storeId: string,
86
86
  tenantId: string,
87
+ level?: CreditPlanLevelEnum,
88
+ segmentId?: string,
87
89
  ): Promise<StandardResponse<PromotedSkusResponse>> {
88
- const url = `${this.baseUrl}/private/promotions/promoted-skus?storeId=${encodeURIComponent(storeId)}&tenantId=${encodeURIComponent(tenantId)}`;
90
+ let url = `${this.baseUrl}/private/promotions/promoted-skus?storeId=${encodeURIComponent(storeId)}&tenantId=${encodeURIComponent(tenantId)}`;
91
+ if (level) url += `&level=${encodeURIComponent(level)}`;
92
+ if (segmentId) url += `&segmentId=${encodeURIComponent(segmentId)}`;
89
93
  return await this.httpRequest.get(url);
90
94
  }
91
95
 
@@ -120,10 +120,15 @@ export interface ILoanOfferingsBusinessApi {
120
120
  * El filtrado vive en el lambda, no acá: solo entran las promociones que alcanzan a CUALQUIER
121
121
  * nivel (el catálogo se pinta antes del score) y las que ACOTAN por SKU (una general marcaría
122
122
  * el catálogo entero). El caller no puede aflojar ese criterio.
123
+ *
124
+ * `level`/`segmentId` son opcionales, mismo criterio que {@link listSkuPromotions}: si no
125
+ * vienen, el lambda destino descarta las promociones que acotan por esa dimensión.
123
126
  */
124
127
  listPromotedSkus(
125
128
  storeId: string,
126
129
  tenantId: string,
130
+ level?: CreditPlanLevelEnum,
131
+ segmentId?: string,
127
132
  ): Promise<StandardResponse<PromotedSkusResponse>>;
128
133
 
129
134
  /**
@@ -5,9 +5,12 @@ import {
5
5
  IMEI_PRODUCT_BATCH_MAX,
6
6
  ImeiProductLookupResponse,
7
7
  IRetailCatalogBusinessApi,
8
+ ProductsByRetailersBody,
9
+ ProductsByRetailersResponse,
8
10
  ReleaseReservationBody,
9
11
  ReserveInventoryBody,
10
12
  ReserveInventoryResult,
13
+ RETAILER_PRODUCTS_BATCH_MAX,
11
14
  SellInventoryBody,
12
15
  StoreCatalogItem,
13
16
  } from "./interfaces/IRetailCatalogBusinessApi.js";
@@ -58,6 +61,21 @@ export default class RetailCatalogBusinessApi implements IRetailCatalogBusinessA
58
61
  return await this.httpRequest.get(`${this.baseUrl}/private/products/by-imei?${query.toString()}`);
59
62
  }
60
63
 
64
+ async getProductsByRetailers(
65
+ retailerIds: string[],
66
+ tenantId: string,
67
+ ): Promise<StandardResponse<ProductsByRetailersResponse>> {
68
+ // Se corta aquí: mandar un lote que el destino va a rechazar solo gasta un round-trip.
69
+ if (retailerIds.length > RETAILER_PRODUCTS_BATCH_MAX) {
70
+ throw new Error(
71
+ `El lote admite hasta ${RETAILER_PRODUCTS_BATCH_MAX} cadenas y llegaron ${retailerIds.length}`,
72
+ );
73
+ }
74
+ const query = new URLSearchParams({ tenantId });
75
+ const body: ProductsByRetailersBody = { retailerIds };
76
+ return await this.httpRequest.post(`${this.baseUrl}/private/products/by-retailers?${query.toString()}`, body);
77
+ }
78
+
61
79
  async reserveInventory(
62
80
  imei: string,
63
81
  body: ReserveInventoryBody,
@@ -93,6 +93,34 @@ export interface ImeiProductLookupResponse {
93
93
  items: ImeiProductLookupEntry[];
94
94
  }
95
95
 
96
+ /** Techo de cadenas por request del lote cadena->catálogo. Lo declara y lo honra el destino. */
97
+ export const RETAILER_PRODUCTS_BATCH_MAX = 100;
98
+
99
+ /** Body de POST `/private/products/by-retailers`. */
100
+ export interface ProductsByRetailersBody {
101
+ retailerIds: string[];
102
+ }
103
+
104
+ /** Un producto vendible de una cadena (espeja `RetailerProductItem` del destino). */
105
+ export interface RetailerProductItem {
106
+ /** Cadena a la que pertenece el producto; es la llave con la que el caller agrupa. */
107
+ retailerId: string;
108
+ sku: string;
109
+ /** Marca comercial (p. ej. "Samsung"). */
110
+ brand: string;
111
+ /** Modelo (p. ej. "A54"). */
112
+ model: string;
113
+ }
114
+
115
+ /** Respuesta del lote cadena->catálogo: los productos de TODAS las cadenas pedidas, en una lista plana. */
116
+ export interface ProductsByRetailersResponse {
117
+ items: RetailerProductItem[];
118
+ /** Cuántos productos trae `items`. */
119
+ count: number;
120
+ /** `true` si el destino recortó el resultado; pide las cadenas en lotes más chicos. */
121
+ truncated: boolean;
122
+ }
123
+
96
124
  /** Resultado de la reserva (espeja `ReserveInventoryResponse` del destino). */
97
125
  export interface ReserveInventoryResult {
98
126
  reservationExpiresAt: string;
@@ -144,6 +172,36 @@ export interface IRetailCatalogBusinessApi {
144
172
  */
145
173
  getProductsByImei(imeis: string[], tenantId: string): Promise<StandardResponse<ImeiProductLookupResponse>>;
146
174
 
175
+ /**
176
+ * POST `/private/products/by-retailers?tenantId=` — catálogo de productos de VARIAS cadenas en
177
+ * una sola llamada, para el consumidor que arma su vista con N cadenas y no quiere N requests.
178
+ *
179
+ * ─────────────────────────────────────────────────────────────────────────────
180
+ * ⚠️ El tenant va por `?tenantId=`, NO por el header `x-tenant-issuer` del resto de este publisher.
181
+ * ─────────────────────────────────────────────────────────────────────────────
182
+ * Es obligatorio. El destino entra por `TenantContextService.withContextByTenantId`, igual que
183
+ * `getProductsByImei`.
184
+ *
185
+ * ── El contrato del lote ────────────────────────────────────────────────────────────────────
186
+ * `retailerIds` no puede venir vacío, sin repetidos y con un techo de
187
+ * `RETAILER_PRODUCTS_BATCH_MAX` cadenas; el caller parte los lotes mayores. La respuesta es una
188
+ * lista PLANA de productos con su `retailerId` — el caller agrupa. Una cadena sin catálogo
189
+ * simplemente no aporta ítems.
190
+ *
191
+ * O el lote sale completo o no sale: si falla la lectura de al menos una cadena el destino
192
+ * responde `INTERNAL_ERROR`. Nunca recibes una lista parcial.
193
+ *
194
+ * @throws Error si el lote supera el techo (se corta aquí, sin salir a la red) ·
195
+ * `400 VALIDATION_ERROR` body vacío, con repetidos, con no-strings o de más de 100 ids ·
196
+ * `400 UNKNOWN_TENANT` falta o no se reconoce el `tenantId` ·
197
+ * `500 TENANT_ASSUME_ROLE_FAILED` fallo de infra del silo ·
198
+ * `500 INTERNAL_ERROR` no se pudo leer alguna cadena.
199
+ */
200
+ getProductsByRetailers(
201
+ retailerIds: string[],
202
+ tenantId: string,
203
+ ): Promise<StandardResponse<ProductsByRetailersResponse>>;
204
+
147
205
  /** PUT `/private/inventory/{imei}/reserve` — reserva atómica del IMEI para una sesión (AVAILABLE → RESERVED). */
148
206
  reserveInventory(imei: string, body: ReserveInventoryBody, issuer: string): Promise<StandardResponse<ReserveInventoryResult>>;
149
207