From 51cfae7572dd5f6ab88cc3bb7943b05f4e066cf1 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 16 Jan 2026 13:13:45 +0100 Subject: [PATCH] Docs y prueba de concepto de colas por empresa --- README.md | 17 +++++ docs/sim-api/Activate.bru | 16 +++++ docs/sim-api/Cancel.bru | 16 +++++ docs/sim-api/Pause.bru | 16 +++++ docs/sim-api/Preactivate.bru | 16 +++++ docs/sim-api/bruno.json | 9 +++ docs/sim-api/environments/local.bru | 3 + packages/shared/domain/EventBus.port.ts | 1 + .../shared/infrastructure/RabbitMQEventBus.ts | 5 ++ packages/sim-consumidor-nos/.env | 22 +++++++ .../aplication/SimNOS.controller.ts | 65 +++++++++++++++++++ .../aplication/SimNOS.usecases.ts | 0 .../sim-consumidor-nos/config/env/index.ts | 22 +++++++ .../config/eventBusConfig.ts | 36 ++++++++++ packages/sim-consumidor-nos/index.ts | 22 +++++++ packages/sim-consumidor-nos/package.json | 32 +++++++++ packages/sim-consumidor-nos/tsconfig.json | 37 +++++++++++ .../infrastructure/simRoutes.http.ts | 2 +- yarn.lock | 22 +++++++ 19 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 docs/sim-api/Activate.bru create mode 100644 docs/sim-api/Cancel.bru create mode 100644 docs/sim-api/Pause.bru create mode 100644 docs/sim-api/Preactivate.bru create mode 100644 docs/sim-api/bruno.json create mode 100644 docs/sim-api/environments/local.bru create mode 100644 packages/sim-consumidor-nos/.env create mode 100644 packages/sim-consumidor-nos/aplication/SimNOS.controller.ts create mode 100644 packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts create mode 100644 packages/sim-consumidor-nos/config/env/index.ts create mode 100644 packages/sim-consumidor-nos/config/eventBusConfig.ts create mode 100644 packages/sim-consumidor-nos/index.ts create mode 100644 packages/sim-consumidor-nos/package.json create mode 100644 packages/sim-consumidor-nos/tsconfig.json diff --git a/README.md b/README.md index f52c1c4..01b7d5e 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,20 @@ Monorepo de servicios / workers para centralizar los procesos de las SIM con sus subscripciones [[./imgs/diagrama-servicios-sim.png]] + +El objetivo es que al lanzar peticiones REST a la parte visible, que se +comprueben y se manden al broker para que los servicios de las compañías +los puedan consumir. + +La idea es que las peticiones de activación, pausa, etc. no necesiten +tener una compañía especificada. + +## Decisiones pendientes + +- [ ] La capa worker según acción y la de operaciones de proveedores + se podrían unir en una sola con un enrutamiento por acción y compañía + pasando de tener claves `sim.[acción]` a `sim.[compañia].[acción]`. +- [ ] La estructura de RMQ se genera por medio del JSON, igual habría que + definir cada cola en el worker que la consuma para poder añadir + workers sin parar el RMQ. +- [ ] Versionado de la API. diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru new file mode 100644 index 0000000..13a3f8c --- /dev/null +++ b/docs/sim-api/Activate.bru @@ -0,0 +1,16 @@ +meta { + name: Activate + type: http + seq: 1 +} + +post { + url: {{baseurl}}/sim/activate + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/docs/sim-api/Cancel.bru b/docs/sim-api/Cancel.bru new file mode 100644 index 0000000..e1ce60a --- /dev/null +++ b/docs/sim-api/Cancel.bru @@ -0,0 +1,16 @@ +meta { + name: Cancel + type: http + seq: 1 +} + +post { + url: {{baseurl}}/sim/cancel + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/docs/sim-api/Pause.bru b/docs/sim-api/Pause.bru new file mode 100644 index 0000000..65d0471 --- /dev/null +++ b/docs/sim-api/Pause.bru @@ -0,0 +1,16 @@ +meta { + name: Pause + type: http + seq: 1 +} + +post { + url: {{baseurl}}/sim/pause + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/docs/sim-api/Preactivate.bru b/docs/sim-api/Preactivate.bru new file mode 100644 index 0000000..3adb322 --- /dev/null +++ b/docs/sim-api/Preactivate.bru @@ -0,0 +1,16 @@ +meta { + name: Preactivate + type: http + seq: 1 +} + +post { + url: {{baseurl}}/sim/preactivate + body: none + auth: inherit +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/docs/sim-api/bruno.json b/docs/sim-api/bruno.json new file mode 100644 index 0000000..fb81b89 --- /dev/null +++ b/docs/sim-api/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "sim-api", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/docs/sim-api/environments/local.bru b/docs/sim-api/environments/local.bru new file mode 100644 index 0000000..84c2813 --- /dev/null +++ b/docs/sim-api/environments/local.bru @@ -0,0 +1,3 @@ +vars { + baseurl: http://locahost +} diff --git a/packages/shared/domain/EventBus.port.ts b/packages/shared/domain/EventBus.port.ts index 4248a53..cdf754c 100644 --- a/packages/shared/domain/EventBus.port.ts +++ b/packages/shared/domain/EventBus.port.ts @@ -8,4 +8,5 @@ export interface EventBus { consume(queue: string, callback: (msg: ConsumeMessage | null) => void): void; ack(msg: ConsumeMessage): void; + nack(msg: ConsumeMessage): void; } diff --git a/packages/shared/infrastructure/RabbitMQEventBus.ts b/packages/shared/infrastructure/RabbitMQEventBus.ts index aaa6fff..cb83bad 100644 --- a/packages/shared/infrastructure/RabbitMQEventBus.ts +++ b/packages/shared/infrastructure/RabbitMQEventBus.ts @@ -37,6 +37,11 @@ export class RabbitMQEventBus implements EventBus { return this.channel.ack(msg) } + nack(msg: ConsumeMessage) { + if (this.channel == undefined) throw new Error("[RMQ] Canal no iniciallizado"); + return this.channel.nack(msg) + } + connection?: ChannelModel channel?: ConfirmChannel connected: Boolean = false diff --git a/packages/sim-consumidor-nos/.env b/packages/sim-consumidor-nos/.env new file mode 100644 index 0000000..28a8d8c --- /dev/null +++ b/packages/sim-consumidor-nos/.env @@ -0,0 +1,22 @@ +PORT=3000 +RABBITMQ_USER=guest +RABBITMQ_PASSWORD=guest + +ENVIORMENT=development + +RABBITMQ_HOST=rabbitmq-sim-broker +#RABBITMQ_HOST=localhost +RABBITMQ_PORT=5672 +RABBITMQ_USER=guest +RABBITMQ_PASSWORD=guest +RABBITMQ_SECURE=false +RABBITMQ_VHOST=sim-vhost + +# Hay cosas que unificar de varios servicios +POSTGRES_DB=postgres +POSTGRES_DATABASE=postres +POSTGRES_HOST=postgresql-sim-1 +POSTGRES_PORT=5432 +DEV_POSTGRES_PORT=5432 +POSTGRES_USER=postgres +POSTGRES_PASSWORD=1234 diff --git a/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts b/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts new file mode 100644 index 0000000..d118ab2 --- /dev/null +++ b/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts @@ -0,0 +1,65 @@ +import { EventBus } from "#shared/domain/EventBus.port"; +import { ConsumeMessage } from "amqplib"; + +export class SimNosController { + private eventBus: EventBus; + private activationUseCases: any; + + private routes = new Map void>([ + ["activate", async () => { console.log("caso de uso activate") }], + ["pause", async () => { console.log("caso de uso pause") }], + ["cancel", async () => { console.log("caso de uso cancel") }], + ]) + + constructor( + eventBus: EventBus + ) { + this.eventBus = eventBus + + // No se si hay un sistema mejor + // convertor en const () => {} para conservar el contexto?? + this.recibeMsg = this.recibeMsg.bind(this) + } + + public async recibeMsg(msg: ConsumeMessage | null) { + if (!this.validateActivationMsg(msg)) { + throw new Error("Error consumiendo el mensaje no es valido") + } + + msg = msg! + + const msgParsed = JSON.parse(String(msg.content)) + const msgKey = msg.fields.routingKey.split(".") + const accion = msgKey[2] + + if (accion == undefined) { + console.error("La routingKey es incorrecta: " + accion) + this.eventBus.nack(msg) + return; + } + + if (this.routes.get(accion) == undefined) { + console.error("No hay una ruta definida para la accion") + this.eventBus.nack(msg) + return; + } + + try { + this.routes.get(accion)!() + } catch (err) { + console.log("Error procesando el mensaje") + this.eventBus.nack(msg) + } finally { + this.eventBus.ack(msg) + } + } + + /** + * TODO: + * - Loguear motivos de la no validacion + */ + private validateActivationMsg(msg: ConsumeMessage | null) { + if (msg == undefined) return false; + return true; + } +} diff --git a/packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts b/packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/sim-consumidor-nos/config/env/index.ts b/packages/sim-consumidor-nos/config/env/index.ts new file mode 100644 index 0000000..f803690 --- /dev/null +++ b/packages/sim-consumidor-nos/config/env/index.ts @@ -0,0 +1,22 @@ +import { loadEnvFile } from "node:process"; +loadEnvFile("../../.env") + +export const env = { + ENVIRONMENT: process.env.ENVIORMENT, + POSTGRES_USER: process.env.POSTGRES_USER, + POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD, + POSTGRES_PORT: process.env.POSTGRES_PORT, + POSTGRES_HOST: process.env.POSTGRES_HOST, + POSTGRES_DATABASE: process.env.POSTGRES_DATABASE, + RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"), + RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "guest"), + RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "guest"), + RABBITMQ_EXCHANGE: String(process.env.RABBITMQ_EXCHANGE ?? "/"), + RABBITMQ_PORT: parseInt(process.env.RABBITMQ_PORT ?? "5672"), + RABBITMQ_MODULENAME: process.env.MODULENAME, + RABBITMQ_TTL: process.env.RABBITMQ_TTL, + RABBITMQ_SECURE: process.env.RABBITMQ_SECURE, + RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL, + RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST), +}; + diff --git a/packages/sim-consumidor-nos/config/eventBusConfig.ts b/packages/sim-consumidor-nos/config/eventBusConfig.ts new file mode 100644 index 0000000..c9a08fc --- /dev/null +++ b/packages/sim-consumidor-nos/config/eventBusConfig.ts @@ -0,0 +1,36 @@ +import { RabbitMQEventBus, RMQConnectionParams } from "#shared/infrastructure/RabbitMQEventBus" +import { env } from "./env" + +const rmqUser = env.RABBITMQ_USER +const rmqPass = env.RABBITMQ_PASSWORD +const rmqHost = env.RABBITMQ_HOST +const rmqPort = Number(env.RABBITMQ_PORT) +const rmqSecure = false +const rmqVhost = env.RABBITMQ_VHOST + +export const rmqConnOptions = { + username: rmqUser, + password: rmqPass, + vhost: rmqVhost, + hostname: rmqHost, + port: rmqPort, + secure: rmqSecure, +} + +export const rabbitmqEventBus = new RabbitMQEventBus({ + connectionParams: rmqConnOptions +}) + +export async function startRMQClient() { + await rabbitmqEventBus.connect() + + // Bindings especificos, deberia meterlos en la clase + try { + rabbitmqEventBus.channel?.assertQueue("sim.nos") + } catch { + console.log("[i] Cola de sims de nos creada") + rabbitmqEventBus.channel?.bindQueue("sim.nos", "sim.exchange", "sim.nos.*") + } + + return rabbitmqEventBus +} diff --git a/packages/sim-consumidor-nos/index.ts b/packages/sim-consumidor-nos/index.ts new file mode 100644 index 0000000..c67c7eb --- /dev/null +++ b/packages/sim-consumidor-nos/index.ts @@ -0,0 +1,22 @@ + +import { startRMQClient } from "#config/eventBusConfig" +import { SimNosController } from "aplication/SimNOS.controller" + +async function startWorker() { + const rmqClient = await startRMQClient() + const simController = new SimNosController( + rmqClient + ) + + rmqClient.consume("sim.nos", simController.recibeMsg) +} + +startWorker() + .then(e => { + console.log("[o] Worker de SIM de NOS iniciado") + }) + .catch(e => { + console.log("[x] Error iniciando worker de SIM de NOS") + }) + +export default {} diff --git a/packages/sim-consumidor-nos/package.json b/packages/sim-consumidor-nos/package.json new file mode 100644 index 0000000..7d1aab3 --- /dev/null +++ b/packages/sim-consumidor-nos/package.json @@ -0,0 +1,32 @@ +{ + "name": "sim-consumidor-nos", + "version": "1.0.0", + "description": "consumidor generico de eventos de NOS", + "main": "index.ts", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "tsx watch index.ts " + }, + "author": "", + "license": "ISC", + "packageManager": "yarn@4.12.0", + "dependencies": { + "@tsconfig/node22": "*", + "amqplib": "^0.10.9", + "cors": "*", + "dotenv": "*", + "express": "*", + "typescript": "*" + }, + "devDependencies": { + "@types/amqplib": "^0.10.8", + "@types/cors": "*", + "@types/express": "*", + "@types/node": "*", + "@types/supertest": "*", + "prettier": "*", + "supertest": "*", + "tsx": "*", + "vitest": "*" + } +} diff --git a/packages/sim-consumidor-nos/tsconfig.json b/packages/sim-consumidor-nos/tsconfig.json new file mode 100644 index 0000000..00ad21b --- /dev/null +++ b/packages/sim-consumidor-nos/tsconfig.json @@ -0,0 +1,37 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "../../dist/sim-consumidor-nos", + "baseUrl": ".", + "paths": { + "#config/*": [ + "config/*" + ], + "#adapters/*": [ + "adapters/*" + ], + "#domain/*": [ + "domain/*" + ], + "#ports/*": [ + "ports/*" + ], + "#tests/*": [ + "__tests__/*" + ], + "#shared/*": [ + "../shared/*" + ], + } + }, + "exclude": [ + "node_modules" + ], + "include": [ + "**/*.ts", + "src/**/*.d.ts" + ], + "files": [ + "index.ts" + ] +} diff --git a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts index efa6c80..9690240 100644 --- a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts +++ b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts @@ -1,6 +1,6 @@ import { rabbitmqEventBus } from '#config/eventBusConfig'; import { SimUsecases } from 'aplication/Sim.usecases'; -import { SimController } from 'aplication/SimController'; +import { SimController } from 'aplication/Sim.controller'; import { Router } from 'express'; const simRoutes = Router() diff --git a/yarn.lock b/yarn.lock index f851539..c8a7724 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2284,6 +2284,28 @@ __metadata: languageName: node linkType: hard +"sim-consumidor-nos@workspace:packages/sim-consumidor-nos": + version: 0.0.0-use.local + resolution: "sim-consumidor-nos@workspace:packages/sim-consumidor-nos" + dependencies: + "@tsconfig/node22": "npm:*" + "@types/amqplib": "npm:^0.10.8" + "@types/cors": "npm:*" + "@types/express": "npm:*" + "@types/node": "npm:*" + "@types/supertest": "npm:*" + amqplib: "npm:^0.10.9" + cors: "npm:*" + dotenv: "npm:*" + express: "npm:*" + prettier: "npm:*" + supertest: "npm:*" + tsx: "npm:*" + typescript: "npm:*" + vitest: "npm:*" + languageName: unknown + linkType: soft + "sim-consumidor@workspace:packages/sim-consumidor-activaciones": version: 0.0.0-use.local resolution: "sim-consumidor@workspace:packages/sim-consumidor-activaciones"