@fiado/api-invoker 5.22.0 → 5.24.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/card/CardApi.d.ts +3 -2
- package/bin/card/CardApi.js +7 -0
- package/bin/card/interfaces/ICardApi.d.ts +24 -1
- package/bin/platformRbac/resolver/ApiInvokerPermissionResolver.js +12 -1
- package/bin/retailOrg/api/RetailOrgBusinessApi.d.ts +9 -2
- package/bin/retailOrg/api/RetailOrgBusinessApi.js +16 -1
- package/bin/retailOrg/api/interfaces/IRetailOrgBusinessApi.d.ts +45 -3
- package/package.json +1 -1
- package/src/card/CardApi.ts +16 -2
- package/src/card/interfaces/ICardApi.ts +25 -1
- package/src/platformRbac/resolver/ApiInvokerPermissionResolver.ts +12 -1
- package/src/retailOrg/api/RetailOrgBusinessApi.ts +24 -2
- package/src/retailOrg/api/interfaces/IRetailOrgBusinessApi.ts +51 -2
package/bin/card/CardApi.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import { IHttpRequest } from "@fiado/http-client";
|
|
3
|
-
import { CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
2
|
+
import type { IHttpRequest } from "@fiado/http-client";
|
|
3
|
+
import { CardApplicationRequest, CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
4
4
|
import { CardsByStatusResponse, ICardApi } from "./interfaces/ICardApi.js";
|
|
5
5
|
import { Provider } from "@fiado/type-kit/bin/provider/index.js";
|
|
6
6
|
import { InfoSelfVerifiedStatus } from "@fiado/type-kit/bin/identity/index.js";
|
|
@@ -19,4 +19,5 @@ export declare class CardApi implements ICardApi {
|
|
|
19
19
|
updateIssuanceObservation(directoryId: string, input: CardIssuanceObservationUpdateRequest): Promise<ApiGatewayResponse<void>>;
|
|
20
20
|
issuance(directoryId: string, USA_InfoSelfVerified: InfoSelfVerifiedStatus): Promise<void>;
|
|
21
21
|
updateShipping(provider: Provider, shippingId: string, input: UpdateBankAccountCardRequest): Promise<ApiGatewayResponse<void>>;
|
|
22
|
+
cardApplication(directoryId: string, provider: Provider, input: CardApplicationRequest): Promise<void>;
|
|
22
23
|
}
|
package/bin/card/CardApi.js
CHANGED
|
@@ -70,6 +70,13 @@ let CardApi = class CardApi {
|
|
|
70
70
|
const url = `${this.baseUrl}${provider}/shipping/${shippingId}`;
|
|
71
71
|
return await this.httpRequest.put(url, input);
|
|
72
72
|
}
|
|
73
|
+
async cardApplication(directoryId, provider, input) {
|
|
74
|
+
const url = `${this.baseUrl}cards/virtual`;
|
|
75
|
+
// `directoryId` y `provider` van DESPUES del spread a proposito: son los
|
|
76
|
+
// dos campos que el endpoint exige, y un `input` que los trajera con
|
|
77
|
+
// otro valor no debe poder pisarlos.
|
|
78
|
+
await this.httpRequest.post(url, { ...input, directoryId, provider });
|
|
79
|
+
}
|
|
73
80
|
};
|
|
74
81
|
CardApi = __decorate([
|
|
75
82
|
injectable(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import { CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
2
|
+
import { CardApplicationRequest, CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
3
3
|
import { InfoSelfVerifiedStatus } from "@fiado/type-kit/bin/identity/index.js";
|
|
4
4
|
import { Provider } from "@fiado/type-kit/bin/provider/index.js";
|
|
5
5
|
export interface CardsByStatusResponse {
|
|
@@ -17,4 +17,27 @@ export interface ICardApi {
|
|
|
17
17
|
updateIssuanceObservation(directoryId: string, input: CardIssuanceObservationUpdateRequest): Promise<ApiGatewayResponse<void>>;
|
|
18
18
|
issuance(directoryId: string, USA_InfoSelfVerified: InfoSelfVerifiedStatus): Promise<void>;
|
|
19
19
|
updateShipping(provider: Provider, shippingId: string, input: UpdateBankAccountCardRequest): Promise<ApiGatewayResponse<void>>;
|
|
20
|
+
/**
|
|
21
|
+
* Emite la tarjeta virtual de un directorio y la registra en
|
|
22
|
+
* `fiado-card-business`, invocable SERVICIO-A-SERVICIO.
|
|
23
|
+
*
|
|
24
|
+
* Hasta esta version esta interfaz declaraba diez metodos y **ninguno
|
|
25
|
+
* creaba tarjetas**: la creacion existia solo en tres rutas humanas
|
|
26
|
+
* (`publicCardApplication`, `agentsCardApplication`,
|
|
27
|
+
* `backofficeCardApplication`), detras de autorizacion de usuario o de
|
|
28
|
+
* agente. Eso dejaba a `account-issuance-business` —que crea la cuenta de
|
|
29
|
+
* Pomelo— sin forma de completarla con su tarjeta.
|
|
30
|
+
*
|
|
31
|
+
* `directoryId` y `provider` viajan en el BODY, no en la ruta: el endpoint
|
|
32
|
+
* es de servicio y no hay token del que sacar el directorio, a diferencia
|
|
33
|
+
* de `publicCardApplication`.
|
|
34
|
+
*
|
|
35
|
+
* **Es idempotente del lado del servidor**: si el directorio ya tiene una
|
|
36
|
+
* tarjeta virtual no cancelada para ese proveedor, no se llama al proveedor
|
|
37
|
+
* y responde 200 igual. El llamador puede reintentar sin duplicar.
|
|
38
|
+
*
|
|
39
|
+
* Devuelve `void`: la ruta no responde la tarjeta creada, asi que el exito
|
|
40
|
+
* es "no lanzo". Un fallo llega como error HTTP.
|
|
41
|
+
*/
|
|
42
|
+
cardApplication(directoryId: string, provider: Provider, input: CardApplicationRequest): Promise<void>;
|
|
20
43
|
}
|
|
@@ -38,7 +38,18 @@ let ApiInvokerPermissionResolver = class ApiInvokerPermissionResolver {
|
|
|
38
38
|
}
|
|
39
39
|
async resolve(input) {
|
|
40
40
|
const cacheable = input.roleAssignmentsFromToken === undefined;
|
|
41
|
-
|
|
41
|
+
// DEC-RBAC-124: el tipo de principal entra en la cache key. NO es defensa contra colisión
|
|
42
|
+
// —los subs de integración llevan prefijo `INT#` y no chocan con un UUID de Cognito— sino
|
|
43
|
+
// para que el canal quede explícito y un cambio futuro de formato no cause aliasing
|
|
44
|
+
// silencioso entre un usuario y una integración.
|
|
45
|
+
//
|
|
46
|
+
// Se lee con cast en vez de tiparlo desde `ResolveInput` A PROPÓSITO: `principalType` recién
|
|
47
|
+
// existe en `@fiado/gateway-adapter` 4.x, y esta lib la consume toda la flota. Atarla al
|
|
48
|
+
// prerelease obligaría a todos a subir de major. Así compila igual contra 3.x y 4.x, y el
|
|
49
|
+
// reenvío al endpoint privado funciona en runtime porque `resolvePermissions(input)` manda
|
|
50
|
+
// el objeto entero sin filtrar campos.
|
|
51
|
+
const principalType = input.principalType ?? "USER";
|
|
52
|
+
const key = `${principalType}#${input.issuer ?? ""}#${input.cognitoSub}`;
|
|
42
53
|
if (cacheable) {
|
|
43
54
|
const hit = this._cache.get(key);
|
|
44
55
|
if (hit)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { IHttpRequest } from "@fiado/http-client";
|
|
2
2
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
3
3
|
import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
4
|
-
import { IRetailOrgBusinessApi } from "./interfaces/IRetailOrgBusinessApi.js";
|
|
4
|
+
import { IRetailOrgBusinessApi, StoreRosterStatusFilter } from "./interfaces/IRetailOrgBusinessApi.js";
|
|
5
5
|
/**
|
|
6
|
-
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus
|
|
6
|
+
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 8
|
|
7
7
|
* endpoints privados de validación. Contrato y semántica completos → `IRetailOrgBusinessApi`.
|
|
8
8
|
*
|
|
9
9
|
* Los paths y el `?tenantId=` fueron verificados contra dev (200 + negativos 400/404) el
|
|
@@ -23,6 +23,13 @@ export default class RetailOrgBusinessApi implements IRetailOrgBusinessApi {
|
|
|
23
23
|
getRetailer(retailerId: string, tenantId: string): Promise<StandardResponse<RetailerValidationDto>>;
|
|
24
24
|
getRetailerByStore(storeId: string, tenantId: string): Promise<StandardResponse<RetailerValidationDto>>;
|
|
25
25
|
getStore(retailerId: string, storeId: string, tenantId: string): Promise<StandardResponse<StoreValidationDto>>;
|
|
26
|
+
/**
|
|
27
|
+
* SureKeep F3 — el padrón de tiendas que le faltaba al tablero de cohorte de «Análisis de
|
|
28
|
+
* ventas»: sin él, la tienda que lleva 60 días sin vender no existe para el tablero. Contrato y
|
|
29
|
+
* semántica completos (incluido por qué acá `status` SÍ tiene default y en `listSellers` no)
|
|
30
|
+
* → `IRetailOrgBusinessApi.listStoresByRetailer`.
|
|
31
|
+
*/
|
|
32
|
+
listStoresByRetailer(retailerId: string, tenantId: string, status?: StoreRosterStatusFilter): Promise<StandardResponse<StoreValidationDto[]>>;
|
|
26
33
|
getStoreUsers(retailerId: string, storeId: string, tenantId: string): Promise<StandardResponse<RetailUserValidationDto[]>>;
|
|
27
34
|
getUserByCognitoSub(cognitoSub: string, tenantId: string): Promise<StandardResponse<RetailUserValidationDto>>;
|
|
28
35
|
listActiveCollectorsByRetailer(retailerId: string, tenantId: string): Promise<StandardResponse<CollectorValidationDto[]>>;
|
|
@@ -12,7 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
12
12
|
};
|
|
13
13
|
import { inject, injectable } from "inversify";
|
|
14
14
|
/**
|
|
15
|
-
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus
|
|
15
|
+
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 8
|
|
16
16
|
* endpoints privados de validación. Contrato y semántica completos → `IRetailOrgBusinessApi`.
|
|
17
17
|
*
|
|
18
18
|
* Los paths y el `?tenantId=` fueron verificados contra dev (200 + negativos 400/404) el
|
|
@@ -43,6 +43,21 @@ let RetailOrgBusinessApi = class RetailOrgBusinessApi {
|
|
|
43
43
|
const url = `${this.baseUrl}/private/retailers/${encodeURIComponent(retailerId)}/stores/${encodeURIComponent(storeId)}?tenantId=${encodeURIComponent(tenantId)}`;
|
|
44
44
|
return await this.httpRequest.get(url);
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* SureKeep F3 — el padrón de tiendas que le faltaba al tablero de cohorte de «Análisis de
|
|
48
|
+
* ventas»: sin él, la tienda que lleva 60 días sin vender no existe para el tablero. Contrato y
|
|
49
|
+
* semántica completos (incluido por qué acá `status` SÍ tiene default y en `listSellers` no)
|
|
50
|
+
* → `IRetailOrgBusinessApi.listStoresByRetailer`.
|
|
51
|
+
*/
|
|
52
|
+
async listStoresByRetailer(retailerId, tenantId, status) {
|
|
53
|
+
// `URLSearchParams` en vez de concatenar: `status` es opcional y armar el `&status=` a mano
|
|
54
|
+
// deja un `&status=undefined` en cuanto alguien se distrae.
|
|
55
|
+
const query = new URLSearchParams({ tenantId });
|
|
56
|
+
if (status)
|
|
57
|
+
query.set("status", status);
|
|
58
|
+
const path = `/private/retailers/${encodeURIComponent(retailerId)}/stores`;
|
|
59
|
+
return await this.httpRequest.get(`${this.baseUrl}${path}?${query.toString()}`);
|
|
60
|
+
}
|
|
46
61
|
async getStoreUsers(retailerId, storeId, tenantId) {
|
|
47
62
|
const url = `${this.baseUrl}/private/retailers/${encodeURIComponent(retailerId)}/users/by-store/${encodeURIComponent(storeId)}?tenantId=${encodeURIComponent(tenantId)}`;
|
|
48
63
|
return await this.httpRequest.get(url);
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { StandardResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
2
|
+
import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, CollectorValidationDto, PrivateSellerListResponse, RetailUserStatusEnum, StoreStatusEnum } from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
3
|
+
/**
|
|
4
|
+
* Filtro de `listStoresByRetailer`. `"ALL"` no es un estado de tienda: es la ausencia de filtro, y
|
|
5
|
+
* va en el MISMO parámetro que los estados porque los dos hablan del mismo eje — un segundo flag
|
|
6
|
+
* tipo `includeInactive` admitiría combinaciones sin sentido que alguien tendría que resolver.
|
|
7
|
+
*/
|
|
8
|
+
export type StoreRosterStatusFilter = StoreStatusEnum | "ALL";
|
|
3
9
|
/**
|
|
4
10
|
* Contrato del publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1)
|
|
5
|
-
* para sus
|
|
11
|
+
* para sus 8 endpoints privados de VALIDACIÓN (service-to-service, VPC-only).
|
|
6
12
|
*
|
|
7
13
|
* Consumidores previstos (regla R5 — cruces entre lambdas son SOLO lecturas de validación;
|
|
8
14
|
* ningún lambda escribe en tabla ajena):
|
|
@@ -12,7 +18,7 @@ import { RetailerValidationDto, StoreValidationDto, RetailUserValidationDto, Col
|
|
|
12
18
|
* - `loan-collector-assignment-business` → lista cobradores ACTIVE del retailer para asignar cartera
|
|
13
19
|
*
|
|
14
20
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
15
|
-
* `tenantId` es OBLIGATORIO en los
|
|
21
|
+
* `tenantId` es OBLIGATORIO en TODOS los métodos — no es un detalle de firma.
|
|
16
22
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
17
23
|
* Los privados NO llevan token de usuario (`Feature.ANONIMUS`; la protección es la red VPC),
|
|
18
24
|
* así que el lambda destino no tiene `AuthContext.issuer` y NO puede inferir el silo. El caller
|
|
@@ -75,6 +81,42 @@ export interface IRetailOrgBusinessApi {
|
|
|
75
81
|
* @throws si la tienda no existe o es de OTRO retailer (`404 STORE_NOT_FOUND` — verificado en dev).
|
|
76
82
|
*/
|
|
77
83
|
getStore(retailerId: string, storeId: string, tenantId: string): Promise<StandardResponse<StoreValidationDto>>;
|
|
84
|
+
/**
|
|
85
|
+
* `GET /private/retailers/{retailerId}/stores` (SureKeep F3) — el **padrón de tiendas** de una
|
|
86
|
+
* cadena.
|
|
87
|
+
*
|
|
88
|
+
* ── 🔴 Por qué este método existe ────────────────────────────────────────────────────────────
|
|
89
|
+
* Lo consume el tablero de cohorte de «Análisis de ventas» (`retail-wizard-business`), que arma
|
|
90
|
+
* la lista de tiendas a partir de las VENTAS que leyó. Con eso, **la tienda que lleva 60 días sin
|
|
91
|
+
* vender no existe para el tablero** — y es exactamente la que hay que mirar. Este es el único
|
|
92
|
+
* endpoint que sirve el padrón completo: `getStore` resuelve UNA tienda y `publicListStores` va
|
|
93
|
+
* detrás del `W4Authorizer` (token de usuario, no invocable entre lambdas).
|
|
94
|
+
*
|
|
95
|
+
* NO se deriva de `listSellers`: ese endpoint escribe en `SharedPiiAccessLog_GT` y esa bitácora
|
|
96
|
+
* es la única traza de una exportación masiva de datos de empleados. Pedirle ids de tienda la
|
|
97
|
+
* llenaría de filas con una intención falsa.
|
|
98
|
+
*
|
|
99
|
+
* Devuelve el mismo `StoreValidationDto` que `getStore` — `storeId`, `retailerId`, `zoneId`,
|
|
100
|
+
* `zoneName`, `code`, `name`, `status` — con el `status` EFECTIVO (cascada del retailer padre ya
|
|
101
|
+
* aplicada). El `zoneName` sale de la tabla de zonas del propio retail-org: el consumidor no
|
|
102
|
+
* puede resolver un `zoneId` de otro dominio, y hoy lo saca de un snapshot congelado en la venta,
|
|
103
|
+
* así que una tienda re-zonificada muestra la zona vieja.
|
|
104
|
+
*
|
|
105
|
+
* ── ⚠️ `status` SÍ tiene default, al revés que `listSellers` ─────────────────────────────────
|
|
106
|
+
* Omitirlo devuelve **solo las `ACTIVE`**. La asimetría es deliberada: allá un vendedor dado de
|
|
107
|
+
* baja el día 20 sí tiene comisión por lo que vendió hasta el 19, así que filtrar lo perdería.
|
|
108
|
+
* Acá el consumidor cruza este padrón con ventas que YA leyó, o sea que la tienda cerrada que
|
|
109
|
+
* vendió dentro de la ventana entra igual por el lado de las ventas — filtrar no puede perder
|
|
110
|
+
* una tienda que vendió, solo evita agregar un cero permanente a un tablero cuyo trabajo es que
|
|
111
|
+
* las filas en cero llamen la atención. Pasa `"ALL"` para el padrón completo.
|
|
112
|
+
*
|
|
113
|
+
* @throws `404 RETAILER_NOT_FOUND` si el retailer no existe en ese silo. Un `[]` no distinguiría
|
|
114
|
+
* "esta cadena no tiene tiendas" de "preguntaste por una cadena que no existe", y lo segundo
|
|
115
|
+
* dejaría el tablero mudo sin que nadie sospeche del request.
|
|
116
|
+
* @throws `400 STORE_STATUS_FILTER_INVALID` si `status` no es del catálogo · `400 UNKNOWN_TENANT`
|
|
117
|
+
* sin `tenantId`.
|
|
118
|
+
*/
|
|
119
|
+
listStoresByRetailer(retailerId: string, tenantId: string, status?: StoreRosterStatusFilter): Promise<StandardResponse<StoreValidationDto[]>>;
|
|
78
120
|
/**
|
|
79
121
|
* GET /private/retailers/{retailerId}/users/by-store/{storeId} — usuarios retail cuya tienda base
|
|
80
122
|
* (`homeStoreId`) es esa. Devuelve `[]` (no 404) si la tienda no tiene usuarios asignados.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiado/api-invoker",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.24.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",
|
package/src/card/CardApi.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import
|
|
3
|
-
|
|
2
|
+
// `import type`: `IHttpRequest` es una interfaz, y el import de VALOR rompe
|
|
3
|
+
// bajo ESM en jest ("does not provide an export named 'IHttpRequest'"), que es
|
|
4
|
+
// lo que impedia testear esta clase. Mismo estilo que los demas Api del repo
|
|
5
|
+
// que si tienen suite (p. ej. `RetailCardsBusinessApi`). No cambia el runtime:
|
|
6
|
+
// tsc borra las dos formas por igual.
|
|
7
|
+
import type { IHttpRequest } from "@fiado/http-client";
|
|
8
|
+
import { CardApplicationRequest, CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
4
9
|
import { inject, injectable } from "inversify";
|
|
5
10
|
import { CardsByStatusResponse, ICardApi } from "./interfaces/ICardApi.js";
|
|
6
11
|
import { Provider } from "@fiado/type-kit/bin/provider/index.js";
|
|
@@ -96,6 +101,15 @@ export class CardApi implements ICardApi {
|
|
|
96
101
|
return await this.httpRequest.put(url, input);
|
|
97
102
|
}
|
|
98
103
|
|
|
104
|
+
async cardApplication(directoryId: string, provider: Provider, input: CardApplicationRequest): Promise<void> {
|
|
105
|
+
|
|
106
|
+
const url = `${this.baseUrl}cards/virtual`;
|
|
107
|
+
// `directoryId` y `provider` van DESPUES del spread a proposito: son los
|
|
108
|
+
// dos campos que el endpoint exige, y un `input` que los trajera con
|
|
109
|
+
// otro valor no debe poder pisarlos.
|
|
110
|
+
await this.httpRequest.post(url, { ...input, directoryId, provider });
|
|
111
|
+
}
|
|
112
|
+
|
|
99
113
|
}
|
|
100
114
|
|
|
101
115
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ApiGatewayResponse } from "@fiado/gateway-adapter";
|
|
2
|
-
import { CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
2
|
+
import { CardApplicationRequest, CardIssuanceObservationUpdateRequest, CardResponse, CardUpdateIssuanceRequest, Status, UpdateBankAccountCardRequest } from "@fiado/type-kit/bin/card/index.js";
|
|
3
3
|
import { InfoSelfVerifiedStatus } from "@fiado/type-kit/bin/identity/index.js";
|
|
4
4
|
import { Provider } from "@fiado/type-kit/bin/provider/index.js";
|
|
5
5
|
|
|
@@ -30,4 +30,28 @@ export interface ICardApi {
|
|
|
30
30
|
|
|
31
31
|
updateShipping(provider: Provider, shippingId: string, input: UpdateBankAccountCardRequest): Promise<ApiGatewayResponse<void>>;
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Emite la tarjeta virtual de un directorio y la registra en
|
|
35
|
+
* `fiado-card-business`, invocable SERVICIO-A-SERVICIO.
|
|
36
|
+
*
|
|
37
|
+
* Hasta esta version esta interfaz declaraba diez metodos y **ninguno
|
|
38
|
+
* creaba tarjetas**: la creacion existia solo en tres rutas humanas
|
|
39
|
+
* (`publicCardApplication`, `agentsCardApplication`,
|
|
40
|
+
* `backofficeCardApplication`), detras de autorizacion de usuario o de
|
|
41
|
+
* agente. Eso dejaba a `account-issuance-business` —que crea la cuenta de
|
|
42
|
+
* Pomelo— sin forma de completarla con su tarjeta.
|
|
43
|
+
*
|
|
44
|
+
* `directoryId` y `provider` viajan en el BODY, no en la ruta: el endpoint
|
|
45
|
+
* es de servicio y no hay token del que sacar el directorio, a diferencia
|
|
46
|
+
* de `publicCardApplication`.
|
|
47
|
+
*
|
|
48
|
+
* **Es idempotente del lado del servidor**: si el directorio ya tiene una
|
|
49
|
+
* tarjeta virtual no cancelada para ese proveedor, no se llama al proveedor
|
|
50
|
+
* y responde 200 igual. El llamador puede reintentar sin duplicar.
|
|
51
|
+
*
|
|
52
|
+
* Devuelve `void`: la ruta no responde la tarjeta creada, asi que el exito
|
|
53
|
+
* es "no lanzo". Un fallo llega como error HTTP.
|
|
54
|
+
*/
|
|
55
|
+
cardApplication(directoryId: string, provider: Provider, input: CardApplicationRequest): Promise<void>;
|
|
56
|
+
|
|
33
57
|
}
|
|
@@ -31,7 +31,18 @@ export class ApiInvokerPermissionResolver {
|
|
|
31
31
|
}
|
|
32
32
|
async resolve(input: ResolveInput): Promise<ResolvedPermissions> {
|
|
33
33
|
const cacheable = input.roleAssignmentsFromToken === undefined;
|
|
34
|
-
|
|
34
|
+
// DEC-RBAC-124: el tipo de principal entra en la cache key. NO es defensa contra colisión
|
|
35
|
+
// —los subs de integración llevan prefijo `INT#` y no chocan con un UUID de Cognito— sino
|
|
36
|
+
// para que el canal quede explícito y un cambio futuro de formato no cause aliasing
|
|
37
|
+
// silencioso entre un usuario y una integración.
|
|
38
|
+
//
|
|
39
|
+
// Se lee con cast en vez de tiparlo desde `ResolveInput` A PROPÓSITO: `principalType` recién
|
|
40
|
+
// existe en `@fiado/gateway-adapter` 4.x, y esta lib la consume toda la flota. Atarla al
|
|
41
|
+
// prerelease obligaría a todos a subir de major. Así compila igual contra 3.x y 4.x, y el
|
|
42
|
+
// reenvío al endpoint privado funciona en runtime porque `resolvePermissions(input)` manda
|
|
43
|
+
// el objeto entero sin filtrar campos.
|
|
44
|
+
const principalType = (input as { principalType?: string }).principalType ?? "USER";
|
|
45
|
+
const key = `${principalType}#${input.issuer ?? ""}#${input.cognitoSub}`;
|
|
35
46
|
if (cacheable) {
|
|
36
47
|
const hit = this._cache.get(key);
|
|
37
48
|
if (hit) return hit;
|
|
@@ -9,10 +9,13 @@ import {
|
|
|
9
9
|
PrivateSellerListResponse,
|
|
10
10
|
RetailUserStatusEnum,
|
|
11
11
|
} from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
IRetailOrgBusinessApi,
|
|
14
|
+
StoreRosterStatusFilter,
|
|
15
|
+
} from "./interfaces/IRetailOrgBusinessApi.js";
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
|
-
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus
|
|
18
|
+
* Publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1) para sus 8
|
|
16
19
|
* endpoints privados de validación. Contrato y semántica completos → `IRetailOrgBusinessApi`.
|
|
17
20
|
*
|
|
18
21
|
* Los paths y el `?tenantId=` fueron verificados contra dev (200 + negativos 400/404) el
|
|
@@ -56,6 +59,25 @@ export default class RetailOrgBusinessApi implements IRetailOrgBusinessApi {
|
|
|
56
59
|
return await this.httpRequest.get(url);
|
|
57
60
|
}
|
|
58
61
|
|
|
62
|
+
/**
|
|
63
|
+
* SureKeep F3 — el padrón de tiendas que le faltaba al tablero de cohorte de «Análisis de
|
|
64
|
+
* ventas»: sin él, la tienda que lleva 60 días sin vender no existe para el tablero. Contrato y
|
|
65
|
+
* semántica completos (incluido por qué acá `status` SÍ tiene default y en `listSellers` no)
|
|
66
|
+
* → `IRetailOrgBusinessApi.listStoresByRetailer`.
|
|
67
|
+
*/
|
|
68
|
+
async listStoresByRetailer(
|
|
69
|
+
retailerId: string,
|
|
70
|
+
tenantId: string,
|
|
71
|
+
status?: StoreRosterStatusFilter,
|
|
72
|
+
): Promise<StandardResponse<StoreValidationDto[]>> {
|
|
73
|
+
// `URLSearchParams` en vez de concatenar: `status` es opcional y armar el `&status=` a mano
|
|
74
|
+
// deja un `&status=undefined` en cuanto alguien se distrae.
|
|
75
|
+
const query = new URLSearchParams({ tenantId });
|
|
76
|
+
if (status) query.set("status", status);
|
|
77
|
+
const path = `/private/retailers/${encodeURIComponent(retailerId)}/stores`;
|
|
78
|
+
return await this.httpRequest.get(`${this.baseUrl}${path}?${query.toString()}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
59
81
|
async getStoreUsers(
|
|
60
82
|
retailerId: string,
|
|
61
83
|
storeId: string,
|
|
@@ -6,11 +6,19 @@ import {
|
|
|
6
6
|
CollectorValidationDto,
|
|
7
7
|
PrivateSellerListResponse,
|
|
8
8
|
RetailUserStatusEnum,
|
|
9
|
+
StoreStatusEnum,
|
|
9
10
|
} from "@fiado/type-kit/bin/retailOrg/index.js";
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Filtro de `listStoresByRetailer`. `"ALL"` no es un estado de tienda: es la ausencia de filtro, y
|
|
14
|
+
* va en el MISMO parámetro que los estados porque los dos hablan del mismo eje — un segundo flag
|
|
15
|
+
* tipo `includeInactive` admitiría combinaciones sin sentido que alguien tendría que resolver.
|
|
16
|
+
*/
|
|
17
|
+
export type StoreRosterStatusFilter = StoreStatusEnum | "ALL";
|
|
18
|
+
|
|
11
19
|
/**
|
|
12
20
|
* Contrato del publisher HTTP del lambda `retail-org-business` (componente 04 SureKeep Fase 1)
|
|
13
|
-
* para sus
|
|
21
|
+
* para sus 8 endpoints privados de VALIDACIÓN (service-to-service, VPC-only).
|
|
14
22
|
*
|
|
15
23
|
* Consumidores previstos (regla R5 — cruces entre lambdas son SOLO lecturas de validación;
|
|
16
24
|
* ningún lambda escribe en tabla ajena):
|
|
@@ -20,7 +28,7 @@ import {
|
|
|
20
28
|
* - `loan-collector-assignment-business` → lista cobradores ACTIVE del retailer para asignar cartera
|
|
21
29
|
*
|
|
22
30
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
-
* `tenantId` es OBLIGATORIO en los
|
|
31
|
+
* `tenantId` es OBLIGATORIO en TODOS los métodos — no es un detalle de firma.
|
|
24
32
|
* ─────────────────────────────────────────────────────────────────────────────
|
|
25
33
|
* Los privados NO llevan token de usuario (`Feature.ANONIMUS`; la protección es la red VPC),
|
|
26
34
|
* así que el lambda destino no tiene `AuthContext.issuer` y NO puede inferir el silo. El caller
|
|
@@ -96,6 +104,47 @@ export interface IRetailOrgBusinessApi {
|
|
|
96
104
|
tenantId: string,
|
|
97
105
|
): Promise<StandardResponse<StoreValidationDto>>;
|
|
98
106
|
|
|
107
|
+
/**
|
|
108
|
+
* `GET /private/retailers/{retailerId}/stores` (SureKeep F3) — el **padrón de tiendas** de una
|
|
109
|
+
* cadena.
|
|
110
|
+
*
|
|
111
|
+
* ── 🔴 Por qué este método existe ────────────────────────────────────────────────────────────
|
|
112
|
+
* Lo consume el tablero de cohorte de «Análisis de ventas» (`retail-wizard-business`), que arma
|
|
113
|
+
* la lista de tiendas a partir de las VENTAS que leyó. Con eso, **la tienda que lleva 60 días sin
|
|
114
|
+
* vender no existe para el tablero** — y es exactamente la que hay que mirar. Este es el único
|
|
115
|
+
* endpoint que sirve el padrón completo: `getStore` resuelve UNA tienda y `publicListStores` va
|
|
116
|
+
* detrás del `W4Authorizer` (token de usuario, no invocable entre lambdas).
|
|
117
|
+
*
|
|
118
|
+
* NO se deriva de `listSellers`: ese endpoint escribe en `SharedPiiAccessLog_GT` y esa bitácora
|
|
119
|
+
* es la única traza de una exportación masiva de datos de empleados. Pedirle ids de tienda la
|
|
120
|
+
* llenaría de filas con una intención falsa.
|
|
121
|
+
*
|
|
122
|
+
* Devuelve el mismo `StoreValidationDto` que `getStore` — `storeId`, `retailerId`, `zoneId`,
|
|
123
|
+
* `zoneName`, `code`, `name`, `status` — con el `status` EFECTIVO (cascada del retailer padre ya
|
|
124
|
+
* aplicada). El `zoneName` sale de la tabla de zonas del propio retail-org: el consumidor no
|
|
125
|
+
* puede resolver un `zoneId` de otro dominio, y hoy lo saca de un snapshot congelado en la venta,
|
|
126
|
+
* así que una tienda re-zonificada muestra la zona vieja.
|
|
127
|
+
*
|
|
128
|
+
* ── ⚠️ `status` SÍ tiene default, al revés que `listSellers` ─────────────────────────────────
|
|
129
|
+
* Omitirlo devuelve **solo las `ACTIVE`**. La asimetría es deliberada: allá un vendedor dado de
|
|
130
|
+
* baja el día 20 sí tiene comisión por lo que vendió hasta el 19, así que filtrar lo perdería.
|
|
131
|
+
* Acá el consumidor cruza este padrón con ventas que YA leyó, o sea que la tienda cerrada que
|
|
132
|
+
* vendió dentro de la ventana entra igual por el lado de las ventas — filtrar no puede perder
|
|
133
|
+
* una tienda que vendió, solo evita agregar un cero permanente a un tablero cuyo trabajo es que
|
|
134
|
+
* las filas en cero llamen la atención. Pasa `"ALL"` para el padrón completo.
|
|
135
|
+
*
|
|
136
|
+
* @throws `404 RETAILER_NOT_FOUND` si el retailer no existe en ese silo. Un `[]` no distinguiría
|
|
137
|
+
* "esta cadena no tiene tiendas" de "preguntaste por una cadena que no existe", y lo segundo
|
|
138
|
+
* dejaría el tablero mudo sin que nadie sospeche del request.
|
|
139
|
+
* @throws `400 STORE_STATUS_FILTER_INVALID` si `status` no es del catálogo · `400 UNKNOWN_TENANT`
|
|
140
|
+
* sin `tenantId`.
|
|
141
|
+
*/
|
|
142
|
+
listStoresByRetailer(
|
|
143
|
+
retailerId: string,
|
|
144
|
+
tenantId: string,
|
|
145
|
+
status?: StoreRosterStatusFilter,
|
|
146
|
+
): Promise<StandardResponse<StoreValidationDto[]>>;
|
|
147
|
+
|
|
99
148
|
/**
|
|
100
149
|
* GET /private/retailers/{retailerId}/users/by-store/{storeId} — usuarios retail cuya tienda base
|
|
101
150
|
* (`homeStoreId`) es esa. Devuelve `[]` (no 404) si la tienda no tiene usuarios asignados.
|