@fiado/api-invoker 5.71.0 → 5.73.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.
@@ -122,6 +122,8 @@ import { WalletFundingSettlementApi } from "./walletFundingSettlement/index.js";
122
122
  import { WalletFundingRulesApi } from "./wallet-funding-rules/index.js";
123
123
  // SNS/SES connector — envío SMS/MAIL síncrono (restaurado para RBAC — DEC-002 messages-business)
124
124
  import { SnsSesConnectorApi } from "./sns-ses-connector/index.js";
125
+ // Twilio connector — envío WhatsApp/SMS por Twilio (lee TWILIO_CONNECTOR_LAMBDA_URL)
126
+ import { TwilioConnectorApi } from "./twilio-connector/index.js";
125
127
  // Remittance business — pricingResolve (Dashboard Ops Fase B, puente de resolución de precio en send-time)
126
128
  import { RemittanceBusinessApi } from "./remittance-business/index.js";
127
129
  import { BeneficiaryApi } from "./beneficiary/index.js";
@@ -269,6 +271,9 @@ export const apiInvokerBindings = new ContainerModule(({ bind }) => {
269
271
  bind("IWalletFundingRulesApi").to(WalletFundingRulesApi);
270
272
  // SNS/SES connector — envío SMS/MAIL síncrono (restaurado para RBAC — DEC-002 messages-business)
271
273
  bind("ISnsSesConnectorApi").to(SnsSesConnectorApi);
274
+ // Twilio connector — envío WhatsApp/SMS por Twilio, elegido por el `provider` del senderConfig.
275
+ // El consumidor necesita TWILIO_CONNECTOR_LAMBDA_URL desde el SSM `fiado-twilio-connector`.
276
+ bind("ITwilioConnectorApi").to(TwilioConnectorApi);
272
277
  // Remittance business — pricingResolve (Dashboard Ops Fase B, puente de resolución de precio en send-time)
273
278
  bind("IRemittanceBusinessApi").to(RemittanceBusinessApi);
274
279
  // Borrado en cascada del directorio — deleteByDirectoryId por dominio
package/bin/index.d.ts CHANGED
@@ -103,6 +103,7 @@ export * from "./uniteller-connector/index.js";
103
103
  export * from "./walletFundingSettlement/index.js";
104
104
  export * from "./wallet-funding-rules/index.js";
105
105
  export * from "./sns-ses-connector/index.js";
106
+ export * from "./twilio-connector/index.js";
106
107
  export * from "./remittance-business/index.js";
107
108
  export * from "./modelias-connector/index.js";
108
109
  export * from "./beneficiary/index.js";
package/bin/index.js CHANGED
@@ -103,6 +103,7 @@ export * from "./uniteller-connector/index.js";
103
103
  export * from "./walletFundingSettlement/index.js";
104
104
  export * from "./wallet-funding-rules/index.js";
105
105
  export * from "./sns-ses-connector/index.js";
106
+ export * from "./twilio-connector/index.js";
106
107
  export * from "./remittance-business/index.js";
107
108
  export * from "./modelias-connector/index.js";
108
109
  export * from "./beneficiary/index.js";
@@ -9,10 +9,12 @@ export declare class MessagesBusinessApi implements IMessagesBusinessApi {
9
9
  sendMessage(request: SendMessageRequest): Promise<FiadoApiResponse<SendMessageResponse>>;
10
10
  verifyOtp(request: VerifyOtpRequest): Promise<FiadoApiResponse<VerifyOtpResponse>>;
11
11
  getValidatedOtp(directoryId: string, otpCode: string): Promise<FiadoApiResponse<OtpValidatedResponse>>;
12
- createTemplate(request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
13
- listTemplates(filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>>;
14
- getTemplate(id: string): Promise<FiadoApiResponse<TemplateMessageResponse>>;
15
- updateTemplate(id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
16
- deleteTemplate(id: string): Promise<FiadoApiResponse<void>>;
12
+ createTemplate(tenantId: string, request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
13
+ listTemplates(tenantId: string, filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>>;
14
+ getTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<TemplateMessageResponse>>;
15
+ updateTemplate(tenantId: string, id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
16
+ deleteTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<void>>;
17
+ /** Cuelga el `tenantId` del query string de una URL sin query previo. */
18
+ private withTenantId;
17
19
  }
18
20
  export {};
@@ -11,6 +11,9 @@ 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 { joinUrl } from '../../utils/joinUrl.js';
15
+ // El CRUD de plantillas manda el `tenantId` en el query string. En POST y PUT se cuelga de la URL
16
+ // porque el http-client solo expone canal de params en GET y DELETE.
14
17
  let MessagesBusinessApi = class MessagesBusinessApi {
15
18
  httpRequest;
16
19
  baseUrl = process.env.MESSAGES_BUSINESS_URL || '';
@@ -26,20 +29,28 @@ let MessagesBusinessApi = class MessagesBusinessApi {
26
29
  async getValidatedOtp(directoryId, otpCode) {
27
30
  return await this.httpRequest.get(`${this.baseUrl}messages/otp/validated`, { directoryId, otpCode }, { 'operationName': 'privateGetValidatedOtp' });
28
31
  }
29
- async createTemplate(request) {
30
- return await this.httpRequest.post(`${this.baseUrl}templates`, request, { 'operationName': 'backofficeCreateTemplate' });
32
+ async createTemplate(tenantId, request) {
33
+ const url = this.withTenantId(joinUrl(this.baseUrl, 'templates'), tenantId);
34
+ return await this.httpRequest.post(url, request, { 'operationName': 'backofficeCreateTemplate' });
31
35
  }
32
- async listTemplates(filters) {
33
- return await this.httpRequest.get(`${this.baseUrl}templates`, filters, { 'operationName': 'backofficeListTemplates' });
36
+ async listTemplates(tenantId, filters) {
37
+ // El tenant explicito pisa el que venga en los filtros: si no, viajarian dos `tenantId`.
38
+ const params = { ...filters, tenantId };
39
+ return await this.httpRequest.get(joinUrl(this.baseUrl, 'templates'), params, { 'operationName': 'backofficeListTemplates' });
34
40
  }
35
- async getTemplate(id) {
36
- return await this.httpRequest.get(`${this.baseUrl}templates/${id}`, undefined, { 'operationName': 'backofficeGetTemplate' });
41
+ async getTemplate(tenantId, id) {
42
+ return await this.httpRequest.get(joinUrl(this.baseUrl, `templates/${id}`), { tenantId }, { 'operationName': 'backofficeGetTemplate' });
37
43
  }
38
- async updateTemplate(id, request) {
39
- return await this.httpRequest.put(`${this.baseUrl}templates/${id}`, request, { 'operationName': 'backofficeUpdateTemplate' });
44
+ async updateTemplate(tenantId, id, request) {
45
+ const url = this.withTenantId(joinUrl(this.baseUrl, `templates/${id}`), tenantId);
46
+ return await this.httpRequest.put(url, request, { 'operationName': 'backofficeUpdateTemplate' });
40
47
  }
41
- async deleteTemplate(id) {
42
- return await this.httpRequest.delete(`${this.baseUrl}templates/${id}`, undefined, { 'operationName': 'backofficeDeleteTemplate' });
48
+ async deleteTemplate(tenantId, id) {
49
+ return await this.httpRequest.delete(joinUrl(this.baseUrl, `templates/${id}`), { tenantId }, { 'operationName': 'backofficeDeleteTemplate' });
50
+ }
51
+ /** Cuelga el `tenantId` del query string de una URL sin query previo. */
52
+ withTenantId(url, tenantId) {
53
+ return `${url}?tenantId=${encodeURIComponent(tenantId)}`;
43
54
  }
44
55
  };
45
56
  MessagesBusinessApi = __decorate([
@@ -4,10 +4,14 @@ export interface IMessagesBusinessApi {
4
4
  sendMessage(request: SendMessageRequest): Promise<FiadoApiResponse<SendMessageResponse>>;
5
5
  verifyOtp(request: VerifyOtpRequest): Promise<FiadoApiResponse<VerifyOtpResponse>>;
6
6
  getValidatedOtp(directoryId: string, otpCode: string): Promise<FiadoApiResponse<OtpValidatedResponse>>;
7
- createTemplate(request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
8
- listTemplates(filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>>;
9
- getTemplate(id: string): Promise<FiadoApiResponse<TemplateMessageResponse>>;
10
- updateTemplate(id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
11
- deleteTemplate(id: string): Promise<FiadoApiResponse<void>>;
7
+ /**
8
+ * CRUD de plantillas. Los cinco viven en la API privada sin authorizer, asi que el `tenantId`
9
+ * es obligatorio y explicito: el lambda responde 400 TENANT_ID_REQUIRED si no llega.
10
+ */
11
+ createTemplate(tenantId: string, request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
12
+ listTemplates(tenantId: string, filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>>;
13
+ getTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<TemplateMessageResponse>>;
14
+ updateTemplate(tenantId: string, id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
15
+ deleteTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<void>>;
12
16
  }
13
17
  export {};
@@ -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 { joinUrl } from '../../utils/joinUrl.js';
14
15
  let SnsSesConnectorApi = class SnsSesConnectorApi {
15
16
  httpRequest;
16
17
  baseUrl = process.env.SNS_SES_CONNECTOR_LAMBDA_URL || '';
@@ -18,7 +19,7 @@ let SnsSesConnectorApi = class SnsSesConnectorApi {
18
19
  this.httpRequest = httpRequest;
19
20
  }
20
21
  async send(request) {
21
- const url = `${this.baseUrl}send`;
22
+ const url = joinUrl(this.baseUrl, 'send');
22
23
  return await this.httpRequest.post(url, request, { 'operationName': 'privateSend' });
23
24
  }
24
25
  };
@@ -1,4 +1,4 @@
1
- import { IHttpRequest } from "@fiado/http-client";
1
+ import type { IHttpRequest } from "@fiado/http-client";
2
2
  import { ConnectorSendMessageRequest, ConnectorSendMessageResponse } from "@fiado/type-kit/bin/twilioConnector/index.js";
3
3
  import { ITwilioConnectorApi } from "./interfaces/ITwilioConnectorApi.js";
4
4
  type FiadoApiResponse<T> = import("@fiado/type-kit/bin/apiResponse/dtos/FiadoApiResponse.js").default<T>;
@@ -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 { joinUrl } from "../../utils/joinUrl.js";
14
15
  let TwilioConnectorApi = class TwilioConnectorApi {
15
16
  httpRequest;
16
17
  baseUrl = process.env.TWILIO_CONNECTOR_LAMBDA_URL || "";
@@ -18,7 +19,7 @@ let TwilioConnectorApi = class TwilioConnectorApi {
18
19
  this.httpRequest = httpRequest;
19
20
  }
20
21
  async send(request) {
21
- const url = `${this.baseUrl}/messages/send`;
22
+ const url = joinUrl(this.baseUrl, "messages/send");
22
23
  return await this.httpRequest.post(url, request, { operationName: "privateSendMessage" });
23
24
  }
24
25
  };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Une la URL base de un lambda con un path relativo dejando exactamente una barra
3
+ * entre ambos, venga o no la base con barra final desde SSM.
4
+ */
5
+ export declare function joinUrl(baseUrl: string, path: string): string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Une la URL base de un lambda con un path relativo dejando exactamente una barra
3
+ * entre ambos, venga o no la base con barra final desde SSM.
4
+ */
5
+ export function joinUrl(baseUrl, path) {
6
+ const base = baseUrl.replace(/\/+$/, '');
7
+ const relativePath = path.replace(/^\/+/, '');
8
+ return `${base}/${relativePath}`;
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/api-invoker",
3
- "version": "5.71.0",
3
+ "version": "5.73.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",
@@ -187,6 +187,8 @@ import { IWalletFundingSettlementApi, WalletFundingSettlementApi } from "./walle
187
187
  import { IWalletFundingRulesApi, WalletFundingRulesApi } from "./wallet-funding-rules/index.js";
188
188
  // SNS/SES connector — envío SMS/MAIL síncrono (restaurado para RBAC — DEC-002 messages-business)
189
189
  import { ISnsSesConnectorApi, SnsSesConnectorApi } from "./sns-ses-connector/index.js";
190
+ // Twilio connector — envío WhatsApp/SMS por Twilio (lee TWILIO_CONNECTOR_LAMBDA_URL)
191
+ import { ITwilioConnectorApi, TwilioConnectorApi } from "./twilio-connector/index.js";
190
192
  // Remittance business — pricingResolve (Dashboard Ops Fase B, puente de resolución de precio en send-time)
191
193
  import { IRemittanceBusinessApi, RemittanceBusinessApi } from "./remittance-business/index.js";
192
194
  import { IBeneficiaryApi, BeneficiaryApi } from "./beneficiary/index.js";
@@ -360,6 +362,10 @@ export const apiInvokerBindings = new ContainerModule(({ bind }) => {
360
362
  // SNS/SES connector — envío SMS/MAIL síncrono (restaurado para RBAC — DEC-002 messages-business)
361
363
  bind<ISnsSesConnectorApi>("ISnsSesConnectorApi").to(SnsSesConnectorApi);
362
364
 
365
+ // Twilio connector — envío WhatsApp/SMS por Twilio, elegido por el `provider` del senderConfig.
366
+ // El consumidor necesita TWILIO_CONNECTOR_LAMBDA_URL desde el SSM `fiado-twilio-connector`.
367
+ bind<ITwilioConnectorApi>("ITwilioConnectorApi").to(TwilioConnectorApi);
368
+
363
369
  // Remittance business — pricingResolve (Dashboard Ops Fase B, puente de resolución de precio en send-time)
364
370
  bind<IRemittanceBusinessApi>("IRemittanceBusinessApi").to(RemittanceBusinessApi);
365
371
 
package/src/index.ts CHANGED
@@ -103,6 +103,7 @@ export * from "./uniteller-connector/index.js";
103
103
  export * from "./walletFundingSettlement/index.js";
104
104
  export * from "./wallet-funding-rules/index.js";
105
105
  export * from "./sns-ses-connector/index.js";
106
+ export * from "./twilio-connector/index.js";
106
107
  export * from "./remittance-business/index.js";
107
108
  export * from "./modelias-connector/index.js";
108
109
  export * from "./beneficiary/index.js";
@@ -9,7 +9,10 @@ import {
9
9
  TemplateMessageResponse, TemplateMessageListResponse, TemplateMessageListFiltersRequest,
10
10
  } from '@fiado/type-kit/bin/messagesConnector/index.js';
11
11
  import { IMessagesBusinessApi } from './interfaces/IMessagesBusinessApi.js';
12
+ import { joinUrl } from '../../utils/joinUrl.js';
12
13
 
14
+ // El CRUD de plantillas manda el `tenantId` en el query string. En POST y PUT se cuelga de la URL
15
+ // porque el http-client solo expone canal de params en GET y DELETE.
13
16
  @injectable()
14
17
  export class MessagesBusinessApi implements IMessagesBusinessApi {
15
18
  private readonly baseUrl = process.env.MESSAGES_BUSINESS_URL || '';
@@ -28,23 +31,32 @@ export class MessagesBusinessApi implements IMessagesBusinessApi {
28
31
  return await this.httpRequest.get(`${this.baseUrl}messages/otp/validated`, { directoryId, otpCode }, { 'operationName': 'privateGetValidatedOtp' });
29
32
  }
30
33
 
31
- async createTemplate(request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>> {
32
- return await this.httpRequest.post(`${this.baseUrl}templates`, request, { 'operationName': 'backofficeCreateTemplate' });
34
+ async createTemplate(tenantId: string, request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>> {
35
+ const url = this.withTenantId(joinUrl(this.baseUrl, 'templates'), tenantId);
36
+ return await this.httpRequest.post(url, request, { 'operationName': 'backofficeCreateTemplate' });
33
37
  }
34
38
 
35
- async listTemplates(filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>> {
36
- return await this.httpRequest.get(`${this.baseUrl}templates`, filters as Record<string, unknown>, { 'operationName': 'backofficeListTemplates' });
39
+ async listTemplates(tenantId: string, filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>> {
40
+ // El tenant explicito pisa el que venga en los filtros: si no, viajarian dos `tenantId`.
41
+ const params = { ...filters, tenantId } as Record<string, unknown>;
42
+ return await this.httpRequest.get(joinUrl(this.baseUrl, 'templates'), params, { 'operationName': 'backofficeListTemplates' });
37
43
  }
38
44
 
39
- async getTemplate(id: string): Promise<FiadoApiResponse<TemplateMessageResponse>> {
40
- return await this.httpRequest.get(`${this.baseUrl}templates/${id}`, undefined, { 'operationName': 'backofficeGetTemplate' });
45
+ async getTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<TemplateMessageResponse>> {
46
+ return await this.httpRequest.get(joinUrl(this.baseUrl, `templates/${id}`), { tenantId }, { 'operationName': 'backofficeGetTemplate' });
41
47
  }
42
48
 
43
- async updateTemplate(id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>> {
44
- return await this.httpRequest.put(`${this.baseUrl}templates/${id}`, request, { 'operationName': 'backofficeUpdateTemplate' });
49
+ async updateTemplate(tenantId: string, id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>> {
50
+ const url = this.withTenantId(joinUrl(this.baseUrl, `templates/${id}`), tenantId);
51
+ return await this.httpRequest.put(url, request, { 'operationName': 'backofficeUpdateTemplate' });
45
52
  }
46
53
 
47
- async deleteTemplate(id: string): Promise<FiadoApiResponse<void>> {
48
- return await this.httpRequest.delete(`${this.baseUrl}templates/${id}`, undefined, { 'operationName': 'backofficeDeleteTemplate' });
54
+ async deleteTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<void>> {
55
+ return await this.httpRequest.delete(joinUrl(this.baseUrl, `templates/${id}`), { tenantId }, { 'operationName': 'backofficeDeleteTemplate' });
56
+ }
57
+
58
+ /** Cuelga el `tenantId` del query string de una URL sin query previo. */
59
+ private withTenantId(url: string, tenantId: string): string {
60
+ return `${url}?tenantId=${encodeURIComponent(tenantId)}`;
49
61
  }
50
62
  }
@@ -12,9 +12,13 @@ export interface IMessagesBusinessApi {
12
12
  verifyOtp(request: VerifyOtpRequest): Promise<FiadoApiResponse<VerifyOtpResponse>>;
13
13
  getValidatedOtp(directoryId: string, otpCode: string): Promise<FiadoApiResponse<OtpValidatedResponse>>;
14
14
 
15
- createTemplate(request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
16
- listTemplates(filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>>;
17
- getTemplate(id: string): Promise<FiadoApiResponse<TemplateMessageResponse>>;
18
- updateTemplate(id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
19
- deleteTemplate(id: string): Promise<FiadoApiResponse<void>>;
15
+ /**
16
+ * CRUD de plantillas. Los cinco viven en la API privada sin authorizer, asi que el `tenantId`
17
+ * es obligatorio y explicito: el lambda responde 400 TENANT_ID_REQUIRED si no llega.
18
+ */
19
+ createTemplate(tenantId: string, request: TemplateMessageCreateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
20
+ listTemplates(tenantId: string, filters: TemplateMessageListFiltersRequest): Promise<FiadoApiResponse<TemplateMessageListResponse>>;
21
+ getTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<TemplateMessageResponse>>;
22
+ updateTemplate(tenantId: string, id: string, request: TemplateMessageUpdateRequest): Promise<FiadoApiResponse<TemplateMessageResponse>>;
23
+ deleteTemplate(tenantId: string, id: string): Promise<FiadoApiResponse<void>>;
20
24
  }
@@ -2,6 +2,7 @@ import { inject, injectable } from 'inversify';
2
2
  import type { IHttpRequest } from '@fiado/http-client';
3
3
  import { MessageDeliveryRequest, MessageDeliveryResponse } from '@fiado/type-kit/bin/messaging/index.js';
4
4
  import { ISnsSesConnectorApi } from './interfaces/ISnsSesConnectorApi.js';
5
+ import { joinUrl } from '../../utils/joinUrl.js';
5
6
 
6
7
  type FiadoApiResponse<T> = import('@fiado/type-kit/bin/apiResponse/dtos/FiadoApiResponse.js').default<T>;
7
8
 
@@ -12,7 +13,7 @@ export class SnsSesConnectorApi implements ISnsSesConnectorApi {
12
13
  constructor(@inject('IHttpRequest') private readonly httpRequest: IHttpRequest) {}
13
14
 
14
15
  async send(request: MessageDeliveryRequest): Promise<FiadoApiResponse<MessageDeliveryResponse>> {
15
- const url = `${this.baseUrl}send`;
16
+ const url = joinUrl(this.baseUrl, 'send');
16
17
  return await this.httpRequest.post(url, request, { 'operationName': 'privateSend' });
17
18
  }
18
19
  }
@@ -1,7 +1,8 @@
1
1
  import { inject, injectable } from "inversify";
2
- import { IHttpRequest } from "@fiado/http-client";
2
+ import type { IHttpRequest } from "@fiado/http-client";
3
3
  import { ConnectorSendMessageRequest, ConnectorSendMessageResponse } from "@fiado/type-kit/bin/twilioConnector/index.js";
4
4
  import { ITwilioConnectorApi } from "./interfaces/ITwilioConnectorApi.js";
5
+ import { joinUrl } from "../../utils/joinUrl.js";
5
6
 
6
7
  type FiadoApiResponse<T> = import("@fiado/type-kit/bin/apiResponse/dtos/FiadoApiResponse.js").default<T>;
7
8
 
@@ -12,7 +13,7 @@ export default class TwilioConnectorApi implements ITwilioConnectorApi {
12
13
  constructor(@inject("IHttpRequest") private httpRequest: IHttpRequest) {}
13
14
 
14
15
  async send(request: ConnectorSendMessageRequest): Promise<FiadoApiResponse<ConnectorSendMessageResponse>> {
15
- const url = `${this.baseUrl}/messages/send`;
16
+ const url = joinUrl(this.baseUrl, "messages/send");
16
17
  return await this.httpRequest.post(url, request, { operationName: "privateSendMessage" });
17
18
  }
18
19
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Une la URL base de un lambda con un path relativo dejando exactamente una barra
3
+ * entre ambos, venga o no la base con barra final desde SSM.
4
+ */
5
+ export function joinUrl(baseUrl: string, path: string): string {
6
+ const base = baseUrl.replace(/\/+$/, '');
7
+ const relativePath = path.replace(/^\/+/, '');
8
+ return `${base}/${relativePath}`;
9
+ }