import { ConsumeMessage } from "amqplib"; import { Request, Response } from "express" import { SimAlaiUsecases } from "./SimAlai.usecases.js"; import { EventBus } from "sim-shared/domain/EventBus.port.js"; import { Result } from "sim-shared/domain/Result.js"; import { SimEvents } from "sim-shared/domain/SimEvents.js"; import { iccidValidator } from "./httpValidators.js"; import { alaiSimToCommonSim } from "#domain/transformers.js"; type ErrorUsecase = { msg: string, stackTrace?: string } export class SimAlaiController { constructor( private uscases: SimAlaiUsecases, private eventBus: EventBus, ) { } private validateMsg(msg: ConsumeMessage | null) { if (msg == undefined) return false; const msgData = this.decodeMsg(msg) as SimEvents.general if (msgData == undefined || msgData.payload == undefined) throw new Error("Mensaje invalido") return msgData; } private decodeMsg(msg: ConsumeMessage): object | undefined { if (msg.content == undefined) { console.warn('[Sim.controller] Mensaje vacío'); return undefined; } 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); console.error(Buffer.from(msg.content).toString(("utf8"))) // Aquí podrías decidir devolver el string crudo o null return undefined; } } /** * Metodo duplicado se puede generalizar la a una clase sharedController con las funciones basicas * TODO: meter un check de 429 */ private async tryUseCase (msg: ConsumeMessage, usecase: () => Promise>): Promise> { try { const result = await usecase() if (result.error == undefined) { await this.eventBus.ack(msg) return result } else { console.error("Error procesando el caso de uso (Alai)", result.error) this.eventBus.nack(msg) return result } } catch (e) { console.error("Error general procesando el caso de uso (Alai)") this.eventBus.nack(msg) return { error: { msg: String(e), stackTrace: String(e) } } } } public activate() { return async (msg: ConsumeMessage) => { console.log("[i] Evento activate ", msg.fields) const data = this.validateMsg(msg) as SimEvents.activation const iccid = data.payload.iccid const correlation_id = data.headers?.message_id const externalId = data.payload.orderId const res = await this.tryUseCase(msg, this.uscases.activate({ iccid: iccid, correlation_id: correlation_id, })) return res; } } public preactivate() { return async (msg: ConsumeMessage) => { console.log("[i] Evento preactivate ", msg) const data = this.validateMsg(msg) as SimEvents.preActivation const iccid = data.payload.iccid const correlation_id = data.headers?.message_id const externalId = data.payload.orderId console.log("MSG:", data, data.headers) const res = await this.tryUseCase(msg, this.uscases.preactivate({ iccid: iccid, correlation_id: correlation_id, externalId: externalId })) return res; } } public suspend() { return async (msg: ConsumeMessage) => { console.log("Evento suspend ", msg.fields) const data = this.validateMsg(msg) as SimEvents.suspend const iccid = data.payload.iccid const correlation_id = data.headers?.message_id const res = await this.tryUseCase(msg, this.uscases.suspend({ iccid: iccid, correlation_id: correlation_id })) return res; } } public reActivate() { return async (msg: ConsumeMessage) => { console.log("Evento reActivate ", msg.fields) const data = this.validateMsg(msg) as SimEvents.reActivation const iccid = data.payload.iccid const correlation_id = data.headers?.message_id const res = await this.tryUseCase(msg, this.uscases.reactivate({ iccid: iccid, correlation_id: correlation_id })) return res; } } public terminate() { return async (msg: ConsumeMessage) => { console.log("Evento reActivate ", msg.fields, msg) const data = this.validateMsg(msg) as SimEvents.reActivation const iccid = data.payload.iccid const correlation_id = data.headers?.message_id const res = await this.tryUseCase(msg, this.uscases.terminate({ iccid: iccid, correlation_id: correlation_id })) return res; } } /** * Select especificamente por REST para evitar pasar por las colas. * La respuesta es instantanea no se tiene que registrar como operación. */ public selectREST() { return async (req: Request, res: Response) => { const { query } = req const body = { iccid: query.iccid as string } console.log("Evento select", body) const validateBody = iccidValidator.validate(body); if (validateBody.error != undefined) { res.status(422).json(validateBody) return; } const iccid: string | string[] = body.iccid if (Array.isArray(iccid)) { // TODO: Automatizar la paginacion //const usecaseRes = this.uscases.selectMany({ iccid }) } else { //const usecaseRes = await this.uscases.selectOne(iccid) const usecaseRes = await this.uscases.selectCompleteSim(iccid) if (usecaseRes.error != undefined) { res.status(500).json(usecaseRes) return; } else { const { sim, subscription, imei } = usecaseRes.data const simData = alaiSimToCommonSim(sim, subscription, imei) res.send(simData) return; } } res.status(200).json(validateBody) } } /** public selectPageREST() { return async (req: Request, res: Response) => { const { offset, limit, filter, orderBy } = req.query const params = { offset: (offset != undefined) ? Number(offset) : undefined, limit: (limit != undefined) ? Number(limit) : undefined, filter: (filter != undefined) ? String(filter) : undefined, orderBy: (orderBy != undefined) ? String(orderBy) : undefined } const usecaseRes = await this.uscases.selectPage(params) if (usecaseRes.error != undefined) { res.status(500).json(usecaseRes) return; } else { res.status(200).send(usecaseRes.data) return; } } } **/ }