diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index c2c8f8b..826582c 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -10,12 +10,8 @@ post { auth: inherit } -params:query { - : -} - body:form-urlencoded { - iccid: 12339912344 + iccid: 8933201124059175967 } settings { diff --git a/packages/shared/domain/SimEvents.ts b/packages/shared/domain/SimEvents.ts index aea6668..d096408 100644 --- a/packages/shared/domain/SimEvents.ts +++ b/packages/shared/domain/SimEvents.ts @@ -11,7 +11,7 @@ export namespace SimEvents { } export type activation = DomainEvent & { - key: "sim.activation", + key: `sim.${string}.activation`, payload: { iccid: string }, @@ -20,7 +20,7 @@ export namespace SimEvents { } export type cancelation = DomainEvent & { - key: "sim.cancelation", + key: `sim.${string}.cancelation`, payload: { iccid: string }, diff --git a/packages/shared/infrastructure/HTTPClient.ts b/packages/shared/infrastructure/HTTPClient.ts index 749f302..0b6dbbd 100644 --- a/packages/shared/infrastructure/HTTPClient.ts +++ b/packages/shared/infrastructure/HTTPClient.ts @@ -30,12 +30,12 @@ export class HttpClient { async (config) => { // Idealmente estas condiciones no deberian de darse si mantenemos el // token valido de forma preventiva - const token = this.jwtManager.authToken?.rawToken + const token = await this.jwtManager.getAccessToken() if (token == undefined) throw new Error("No se ha obtenido el token para la peticion") config.headers.Authorization = `Bearer ${this.jwtManager.authToken!.rawToken}` - console.log("request completa", config.headers, config.data) + console.log("request completa", config.data) return config; }, (error) => Promise.reject(error) @@ -49,7 +49,7 @@ export class HttpClient { async (error) => { // TODO: Esta parte no tiene tipos, hay que asegurar el error const req = error.config - console.error("[http] Error en la respuesta ", error) + console.error("[http] Error en la respuesta ", error.response.data) if (error.response?.status == 401) { this.jwtManager.getAccessToken() } diff --git a/packages/shared/infrastructure/RabbitMQEventBus.ts b/packages/shared/infrastructure/RabbitMQEventBus.ts index 308b8da..86de142 100644 --- a/packages/shared/infrastructure/RabbitMQEventBus.ts +++ b/packages/shared/infrastructure/RabbitMQEventBus.ts @@ -122,7 +122,6 @@ export class RabbitMQEventBus implements EventBus { protected async createChannel() { if (this.connection == undefined) throw new Error("[RMQ] Intentando crear un canal sin una conexion") const channel = this.connection.createChannel({ - json: true, setup: (channel: Channel) => { // Exchanges comunes a todos channel.assertExchange("sim.exchange", "topic", { durable: true }) diff --git a/packages/sim-consumidor-objenious/.env b/packages/sim-consumidor-objenious/.env index 85d1f4b..d667a1d 100644 --- a/packages/sim-consumidor-objenious/.env +++ b/packages/sim-consumidor-objenious/.env @@ -4,3 +4,4 @@ OBJ_AUTHORIZATION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSI OBJ_CLI_ASSERTION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ OBJ_CLIENT_ID=savefamily_rest_ws OBJ_KID=xNfbMiyL1ORXGP8lElhcv8nVaG3EJKye4Lc1YoN3I1E +OBJ_BASE_URL=https://api-getway.objenious.com/ws diff --git a/packages/sim-consumidor-objenious/aplication/JWT.service.ts b/packages/sim-consumidor-objenious/aplication/JWT.service.ts index 861147b..36875b7 100644 --- a/packages/sim-consumidor-objenious/aplication/JWT.service.ts +++ b/packages/sim-consumidor-objenious/aplication/JWT.service.ts @@ -118,11 +118,7 @@ export class JWTService { return token } - public async getAccessToken() { - if (this.authToken != undefined && !this.authToken.isExpired()) { - console.warn("Se está intentado conseguir un token sin expirar el anterior") - } - + public async getNewTokens() { const bodyWithtoken = { ...DEFAULT_BODY, client_assertion: this.buildJwtBody() @@ -151,6 +147,25 @@ export class JWTService { } } + public async getAccessToken() { + // Caso 1: El token actual es valido + if (this.authToken != undefined && !this.authToken.isExpired()) { + return this.authToken + } + + // Caso 2: El token ha expirado pero se puede refrescar + if (this.authToken?.isExpired() && this.refreshToken != undefined && !this.refreshToken.isExpired()) { + await this.tryRefreshToken() + } + + // Caso 3: Ningún token es valido + await this.getNewTokens() + + if (this.authToken == undefined) throw new Error("Error obteniendo tokens de auth") + + return this.authToken + } + public async tryRefreshToken() { if (this.refreshToken == undefined) throw new Error("El refreshToken no está definido") if (this.refreshToken.isExpired()) throw new Error("El refreshToken ha expirado") diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index 994237e..058faef 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -1,7 +1,14 @@ import { EventBus } from "#shared/domain/EventBus.port"; import { ConsumeMessage } from "amqplib"; import { SimUseCases } from "./Sim.usecases"; +import { SimEvents } from "#shared/domain/SimEvents"; +/** + * La clase usa generadores de funciones para mantener el contexto + * el proceso se hace en 2 partes: + * Controlador - handlers -> Router -> Ejecucion + * + */ export class SimController { private eventBus: EventBus; private useCases: SimUseCases @@ -15,33 +22,61 @@ export class SimController { } - public async activateSim(msg: ConsumeMessage | null) { - return async () => { - if (!this.validateActivationMsg(msg)) { - throw new Error("Error consumiendo el mensaje no es valido") - } - const msgData = Buffer.from(JSON.parse(msg?.content.toString("utf-8") || "{}").data) - console.log("Mensaje procesado", String(msgData)) + private decodeMsg(msg: ConsumeMessage): object | undefined { + if (msg.content == undefined) { + console.warn('[Sim.controller] Mensaje vacío'); + return undefined; + } - // Caso de uso de activaciones - await this.useCases.activate({ - dueDate: new Date().toString(), - identifier: { - identifierType: "ICCID", - identifiers: ["1234"] - } - })() - // TODO: comprobar el resultado de la opreacion - this.eventBus.ack(msg!) + try { + // Convertir el Buffer a String (UTF-8) + const contentJson = JSON.parse(Buffer.from(msg.content).toString('utf8')) + return contentJson; + + } catch (error) { + console.error('Error al decodificar JSON:', error); + // Aquí podrías decidir devolver el string crudo o null + return undefined; } } - public async pauseSim(msg: ConsumeMessage | null) { - return async () => { + public activateSim() { + return async (msg: ConsumeMessage) => { if (!this.validateActivationMsg(msg)) { throw new Error("Error consumiendo el mensaje no es valido") } - const msgData = Buffer.from(JSON.parse(msg?.content.toString("utf-8") || "{}").data) + const msgData = this.decodeMsg(msg) as SimEvents.activation + + if (msgData == undefined || msgData.payload == undefined) Promise.reject("Mensaje invalido") + console.log("Mensaje procesado", msgData?.toString()) + + // TODO: Añadir un validador del mensaje + + const iccid = msgData.payload.iccid + + try { + // Caso de uso de activaciones + await this.useCases.activate({ + dueDate: this.genDueDate(2 * 60).toISOString(), + identifier: { + identifierType: "ICCID", + identifiers: [iccid] + } + })() + this.eventBus.ack(msg) + } catch (e) { + this.eventBus.nack(msg) + } + } + } + + public pauseSim() { + return async (msg: ConsumeMessage) => { + if (!this.validateActivationMsg(msg)) { + throw new Error("Error consumiendo el mensaje no es valido") + } + const msgData = this.decodeMsg(msg) + if (msgData == undefined) Promise.reject("Mensaje invalido") console.log("Mensaje procesado", String(msgData)) } } @@ -54,4 +89,10 @@ export class SimController { if (msg == undefined) return false; return true; } + + private genDueDate(secondsDue: number) { + const now = Date.now() + secondsDue * 1000 + const dueDate = new Date(now) + return dueDate + } } diff --git a/packages/sim-consumidor-objenious/aplication/Sim.router.ts b/packages/sim-consumidor-objenious/aplication/Sim.router.ts index 99c917e..8d37f56 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.router.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.router.ts @@ -1,48 +1,62 @@ /** - * Dirige cada mensaje dependiendo de el tipo de accion que contenga + * Dirige cada mensaje dependiendo de el tipo de acción que contenga + * Podría hacerse con varias colas, pero así se controla mejor que + * las operaciones se hagan de 1 en 1. */ import { ConsumeMessage } from "amqplib"; import { SimController } from "./Sim.controller"; export class SimRouter { - private simController: SimController + private readonly routes: Map Promise>; - private routeMap: Map void> = new Map() - - constructor(simController: SimController) { - this.simController = simController - - // No me gusta que se defina en el constructor - this.routeMap = new Map([ - ["activate", this.simController.activateSim], - ["pause", this.simController.pauseSim], - ]) - this.route = this.route.bind(this) + constructor(private readonly simController: SimController) { + this.routes = new Map([ + ["activate", this.simController.activateSim()], + ["pause", this.simController.pauseSim()], + ]); } - public route(msg: ConsumeMessage | null) { - if (msg == undefined) { - console.error("Mensaje vacio") + /** + * Enruta el mensaje a la acción correspondiente basándose en la routing key + */ + public route = async (msg: ConsumeMessage | null): Promise => { + if (!msg) { + console.error("[Router] Mensaje vacío"); return; } - const routingKey = msg.fields.routingKey - const action = routingKey.split(".")[2] + const action = this.extractAction(msg); - if (action == undefined) { - console.error("La routing key no tiene una accion definida") - console.error(msg.fields) + if (!action) { + console.error("[Router] La routing key no tiene una acción definida", msg.fields.routingKey); + return; } - const accionEjecutable = this.routeMap.get(action) + const handler = this.routes.get(action); - if (accionEjecutable == undefined) { - console.error("La accion del mensaje no tiene un controlador asociado") - } else { - console.log("Ejecutado operacion", action) + if (!handler) { + console.error(`[Router] La acción '${action}' no tiene un controlador asociado`); + return; } + try { + console.log("[Router] Ejecutando operación:", action); + + // El controlador devuelve una función (thunk) que debe ser ejecutada + const executeParams = await handler(msg); + + if (typeof executeParams === "function") { + await executeParams(); + } + + } catch (error) { + console.error(`[Router] Error al ejecutar la operación '${action}':`, error); + } + }; + + private extractAction(msg: ConsumeMessage): string | undefined { + // Se asume que la acción está en la tercera posición: domain.compañia.accion + return msg.fields.routingKey.split(".")[2]; } - } diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 27a362a..93c9d61 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -15,7 +15,7 @@ export class SimUseCases { } public activate(activationData: ActivationData) { - const OPERATION_URL = "/actions/preactivate" + const OPERATION_URL = "/actions/preactivateLine" return async () => { const req = this.httpClient.client.post(OPERATION_URL, { ...activationData @@ -23,7 +23,7 @@ export class SimUseCases { try { const e = await req - console.log("Activacion con exito", e.data) + console.log("Activacion con exito", e.data.response) } catch (error) { console.error("Error activando ", error) } diff --git a/packages/sim-consumidor-objenious/aplication/SimActivation.usecase.ts b/packages/sim-consumidor-objenious/aplication/SimActivation.usecase.ts deleted file mode 100644 index 454adab..0000000 --- a/packages/sim-consumidor-objenious/aplication/SimActivation.usecase.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ActivationData } from "#domain/DTOs/objeniousapi" -import { HttpClient } from "#shared/infrastructure/HTTPClient" - - -export class SimActivationUseCase { - private httpClient: HttpClient - - constructor(args: { - - httpClient: HttpClient - }) { - this.httpClient = args.httpClient - } - - public async run(activationData: ActivationData,) { - const req = this.httpClient.client.post("/actions/preactivate", { - ...activationData - }) - - try { - const e = await req - console.log("Activacion con exito", e.data) - } catch (error) { - console.error("Error activando ", error) - } - } -} diff --git a/packages/sim-consumidor-objenious/config/env/index.ts b/packages/sim-consumidor-objenious/config/env/index.ts index 820595c..109abf6 100644 --- a/packages/sim-consumidor-objenious/config/env/index.ts +++ b/packages/sim-consumidor-objenious/config/env/index.ts @@ -27,7 +27,8 @@ export const env = { OBJ_AUTHOIZATION: String(process.env.OBJ_ATHORIZATION), OBJ_CLI_ASSERTION: String(process.env.OBJ_CLI_ASSERTION), OBJ_CLIENT_ID: String(process.env.OBJ_CLIENT_ID), - OBJ_KID: String(process.env.OBJ_KID) + OBJ_KID: String(process.env.OBJ_KID), + OBJ_BASE_URL: String(process.env.OBJ_BASE_URL) }; diff --git a/packages/sim-consumidor-objenious/config/httpClient.config.ts b/packages/sim-consumidor-objenious/config/httpClient.config.ts index a346b28..b55e461 100644 --- a/packages/sim-consumidor-objenious/config/httpClient.config.ts +++ b/packages/sim-consumidor-objenious/config/httpClient.config.ts @@ -1,12 +1,13 @@ import { JWTService } from "aplication/JWT.service" import { HttpClient } from "#shared/infrastructure/HTTPClient" +import { env } from "./env" -// TODO: mover a shared/infrastructure para usar en el resto de servicios +const OBJ_BASE_URL = env.OBJ_BASE_URL export const httpInstance = new HttpClient({ - baseURL: "https://api-getway.objenious.com/ws/", + baseURL: OBJ_BASE_URL, headers: { - "content-type": "application/x-www-form-urlencoded" + "content-type": " application/json; charset=utf-8" }, jwtManager: new JWTService() }) diff --git a/packages/sim-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index ae34354..e0475ca 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -10,7 +10,7 @@ const COMPAÑIASICCID = new Map( [ ["3490", "alai"], ["3510", "nos"], - ["3399", "objenious"] + ["3320", "objenious"] ]) export class SimController { diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index 38a94e4..51ac33f 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -46,16 +46,6 @@ export class SimUsecases { return this.eventBus.publish([activationEvent]) } - async cancelation(args: { iccid: string }) { - const cancelationEvent = { - key: "sim.cancelation", - payload: { - iccid: args.iccid - } - } - - return this.eventBus.publish([cancelationEvent]) - } async pause(args: { iccid: string }) { const cancelationEvent = {