298 lines
8.7 KiB
TypeScript
298 lines
8.7 KiB
TypeScript
import { EventBus } from "sim-shared/domain/EventBus.port.js";
|
|
import { ConsumeMessage } from "amqplib";
|
|
import { SimUseCases } from "./Sim.usecases.js";
|
|
import { SimEvents } from "sim-shared/domain/SimEvents.js";
|
|
import { Result } from "sim-shared/domain/Result.js";
|
|
import { ActionData } from "#domain/DTOs/objeniousapi.js";
|
|
import { Request, Response } from "express"
|
|
import { PaginationArgs, QueryPaginationArgs } from "sim-shared/domain/PaginationArgs.js";
|
|
import { paginationValidator } from "./httpValidators.js";
|
|
import { error } from "node:console";
|
|
import { objeniousSimToCommon } from "#domain/transformers.js";
|
|
|
|
/**
|
|
* 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
|
|
|
|
constructor(
|
|
eventBus: EventBus,
|
|
useCases: SimUseCases
|
|
) {
|
|
this.eventBus = eventBus
|
|
this.useCases = useCases
|
|
}
|
|
|
|
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 general de lanzar un caso de uso con un mensaje, si el caso de uso es exitoso
|
|
* se ACK el mesaje, si hay algún error se NACK.
|
|
* TODO:
|
|
* - Se podrian hacer genericos los parametros
|
|
*/
|
|
private async tryUseCase<T extends any>(msg: ConsumeMessage, usecase: () => Promise<Result<string, T>>) {
|
|
try {
|
|
const result = await usecase()
|
|
if (result.error == undefined) {
|
|
await this.eventBus.ack(msg)
|
|
return result
|
|
} else {
|
|
console.error("Error general procesando el caso de uso", result.error)
|
|
this.eventBus.nack(msg)
|
|
}
|
|
} catch (e) {
|
|
console.error("Error general procesando el caso de uso")
|
|
this.eventBus.nack(msg)
|
|
}
|
|
}
|
|
|
|
public activate() {
|
|
const DUE_DATE_SECONDS = 2 * 60
|
|
|
|
return async (msg: ConsumeMessage) => {
|
|
let msgData;
|
|
try {
|
|
msgData = this.validateMsg(msg) as SimEvents.activation
|
|
} catch (e) {
|
|
throw new Error("Error consumiendo el mensaje no es valido" + String(e))
|
|
}
|
|
|
|
const iccid = msgData.payload.iccid
|
|
const offer = msgData.payload.offer
|
|
|
|
if (offer == undefined) {
|
|
this.eventBus.nack(msg)
|
|
throw new Error("Error activando la sim, no se ha especificado la oferta")
|
|
}
|
|
|
|
const resp = await this.tryUseCase(msg, this.useCases.activate({
|
|
correlation_id: msgData.headers?.message_id,
|
|
dueDate: this.genDueDate(DUE_DATE_SECONDS).toISOString(),
|
|
customerAccountCode: "9.49411.10",
|
|
identifier: {
|
|
identifierType: "ICCID",
|
|
identifiers: [iccid]
|
|
},
|
|
offer: {
|
|
code: offer,
|
|
services: []
|
|
}
|
|
}))
|
|
|
|
// TODO:
|
|
// - Crear un registro de operación
|
|
// - Si ha salido bien id de operación -> webhook?
|
|
// - Si ha salido mal notificar solo cuando se manda a dlx ??
|
|
}
|
|
}
|
|
|
|
public preActivate() {
|
|
return async (msg: ConsumeMessage) => {
|
|
let msgData;
|
|
try {
|
|
msgData = this.validateMsg(msg) as SimEvents.suspend
|
|
} catch (e) {
|
|
throw new Error("Error de preactivacion consumiendo el mensaje no es valido" + String(e))
|
|
}
|
|
|
|
if (msgData == undefined) {
|
|
return Promise.reject("Mensaje invalido")
|
|
}
|
|
|
|
const iccid = msgData.payload.iccid
|
|
const res = await this.tryUseCase(msg, this.useCases.preActivate({
|
|
correlation_id: msgData.headers?.message_id,
|
|
dueDate: this.genDueDate(2 * 60).toISOString(),
|
|
identifier: {
|
|
identifierType: "ICCID",
|
|
identifiers: [iccid]
|
|
}
|
|
}))
|
|
|
|
}
|
|
}
|
|
|
|
|
|
public reActivate() {
|
|
return async (msg: ConsumeMessage) => {
|
|
let msgData;
|
|
try {
|
|
msgData = this.validateMsg(msg) as SimEvents.suspend
|
|
} catch (e) {
|
|
throw new Error("Error de reactivacion consumiendo el mensaje no es valido" + String(e))
|
|
}
|
|
|
|
if (msgData == undefined) {
|
|
return Promise.reject("Mensaje invalido")
|
|
}
|
|
|
|
const iccid = msgData.payload.iccid
|
|
const res = await this.tryUseCase(msg, this.useCases.reActivate({
|
|
correlation_id: msgData.headers?.message_id,
|
|
dueDate: this.genDueDate(2 * 60).toISOString(),
|
|
identifier: {
|
|
identifierType: "ICCID",
|
|
identifiers: [iccid]
|
|
}
|
|
}))
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lo mismo que pause
|
|
*/
|
|
public suspend() {
|
|
return async (msg: ConsumeMessage) => {
|
|
let msgData;
|
|
try {
|
|
msgData = this.validateMsg(msg) as SimEvents.suspend
|
|
} catch (e) {
|
|
throw new Error("Error de suspension consumiendo el mensaje no es valido" + String(e))
|
|
}
|
|
|
|
if (msgData == undefined) {
|
|
return Promise.reject("Mensaje invalido")
|
|
}
|
|
|
|
const iccid = msgData.payload.iccid
|
|
const suspendData: ActionData = {
|
|
correlation_id: msgData.headers?.message_id,
|
|
dueDate: this.genDueDate(2 * 60).toISOString(),
|
|
identifier: {
|
|
identifierType: "ICCID",
|
|
identifiers: [iccid] // Por algún motivo solo he puesto un iccd por identifier
|
|
}
|
|
}
|
|
const useCaseRes = await this.tryUseCase(msg, this.useCases.stage_suspend(suspendData))
|
|
/*
|
|
const res = await this.tryUseCase(msg, this.useCases.suspend(actionData))
|
|
*/
|
|
|
|
}
|
|
}
|
|
|
|
public terminate() {
|
|
return async (msg: ConsumeMessage) => {
|
|
let msgData;
|
|
try {
|
|
msgData = this.validateMsg(msg) as SimEvents.suspend
|
|
} catch (e) {
|
|
throw new Error("Error consumiendo el mensaje no es valido" + String(e))
|
|
}
|
|
|
|
if (msgData == undefined) {
|
|
return Promise.reject("Mensaje invalido")
|
|
}
|
|
|
|
const iccid = msgData.payload.iccid
|
|
const terminateActionData: ActionData = {
|
|
correlation_id: msgData.headers?.message_id,
|
|
dueDate: this.genDueDate(2 * 60).toISOString(),
|
|
identifier: {
|
|
identifierType: "ICCID",
|
|
identifiers: [iccid]
|
|
}
|
|
}
|
|
|
|
//const res = await this.tryUseCase(msg, this.useCases.terminate(terminateActionData))
|
|
const res = await this.tryUseCase(msg, this.useCases.stage_terminate(terminateActionData))
|
|
|
|
}
|
|
}
|
|
|
|
public queryLines() {
|
|
const DEFAULT_LIMIT = 1000
|
|
const DEFAULT_OFFSET = 0
|
|
|
|
return async (req: Request, res: Response) => {
|
|
const queryParams = req.query
|
|
|
|
const paginationArgs: QueryPaginationArgs = {
|
|
limit: queryParams.limit as string | undefined,
|
|
offset: queryParams.offset as string | undefined
|
|
}
|
|
|
|
const validationRes = paginationValidator.validate(paginationArgs)
|
|
if (validationRes.error != undefined) {
|
|
res.status(422).json(validationRes)
|
|
return;
|
|
}
|
|
|
|
const paginationValues = {
|
|
limit: (queryParams.limit != undefined) ? Number(queryParams.limit) : DEFAULT_LIMIT,
|
|
offset: (queryParams.offset != undefined) ? Number(queryParams.offset) : DEFAULT_OFFSET
|
|
}
|
|
|
|
const status = req.query.status
|
|
|
|
const queryRes = await this.useCases.getLinesByQuery({ status: status as string | undefined }, paginationValues)
|
|
res.json(queryRes)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Una única linea para /select
|
|
*/
|
|
public queryLine() {
|
|
return async (req: Request, res: Response) => {
|
|
const queryParams = req.query
|
|
const queryArgs = {
|
|
iccid: queryParams.iccid as string // La validacion de iccid se ha tenido que hacer en el gateway
|
|
}
|
|
|
|
const line = await this.useCases.getLineByIccid(queryArgs.iccid)
|
|
if (line.error != undefined) {
|
|
res.status(line.error.code).json(line)
|
|
return;
|
|
}
|
|
|
|
const commonLine = objeniousSimToCommon(line.data)
|
|
|
|
res.status(200).json({ data: commonLine })
|
|
}
|
|
}
|
|
|
|
/**
|
|
* TODO:
|
|
* - Loguear motivos de la no validacion
|
|
* - Añadir validadores inyectables
|
|
*/
|
|
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 genDueDate(secondsDue: number) {
|
|
const now = Date.now() + secondsDue * 1000
|
|
const dueDate = new Date(now)
|
|
return dueDate
|
|
}
|
|
}
|
|
|