From 39c0e87758c50cb443e2707b9c2c453171e31bad Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 4 Mar 2026 13:51:24 +0100 Subject: [PATCH] Mejora de las orders y actualizacion docs --- .env | 8 +- docs/sim-api/Activate.bru | 2 +- docs/sim-api/Cancel.bru | 35 +++ docs/sim-api/collection.bru | 34 +++ packages/sim-consumidor-nos/.env | 17 -- .../aplication/Sim.controller.ts | 231 ++++++------------ .../aplication/Sim.usecases.ts | 90 +++++-- .../aplication/httpValidators.test.ts | 3 +- .../aplication/httpValidators.ts | 4 +- .../sim-objenious-cron/config/env/index.ts | 1 - .../sim-shared/aplication/BodyValidator.ts | 16 +- packages/sim-shared/domain/Result.ts | 37 ++- 12 files changed, 269 insertions(+), 209 deletions(-) create mode 100644 docs/sim-api/collection.bru diff --git a/.env b/.env index c7a4893..681f736 100644 --- a/.env +++ b/.env @@ -5,8 +5,8 @@ RABBITMQ_PASSWORD=guest ENVIORMENT=development -RABBITMQ_HOST=rabbitmq-sim-broker -# RABBITMQ_HOST=localhost +#RABBITMQ_HOST=rabbitmq-sim-broker +RABBITMQ_HOST=localhost RABBITMQ_PORT=5672 RABBITMQ_USER=guest RABBITMQ_PASSWORD=guest @@ -14,8 +14,8 @@ RABBITMQ_SECURE=false RABBITMQ_VHOST=sim-vhost # Hay cosas que unificar de varios servicios -POSTGRES_HOST=postgresql-sim -# POSTGRES_HOST=localhost +#POSTGRES_HOST=postgresql-sim +POSTGRES_HOST=localhost POSTGRES_DB=postgres POSTGRES_DATABASE=postgres POSTGRES_PORT=5433 diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index 42a9d5c..0d4eda1 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125065160331 + iccid: 8933201125068886692 offer: SAVEFAMILY1 } diff --git a/docs/sim-api/Cancel.bru b/docs/sim-api/Cancel.bru index cb35006..4c5584e 100644 --- a/docs/sim-api/Cancel.bru +++ b/docs/sim-api/Cancel.bru @@ -18,3 +18,38 @@ settings { encodeUrl: true timeout: 0 } + +docs { + El endpoint recibe como body + ``` + { + iccid: string, + update_webhook?: string + } + ``` + + `update_webhook` está en desarrollo, pero será donde se mande la actualizacion de la cancelación cuando haya una respuesta de la API externa. + + Si la llamada tiene exito devuelve: + ``` json + { + data: { + iccid: string, + message_id: string, + operation: "cancelation" + } + } + + ``` + message_id se usará para la llamada /orders/message_id/}{message_id} + + Si la llamada falla devolvera: + ```json + { + errors: { + msg: string + ... (campos extra de gestion del error) + } + } + ``` +} diff --git a/docs/sim-api/collection.bru b/docs/sim-api/collection.bru new file mode 100644 index 0000000..5fe677a --- /dev/null +++ b/docs/sim-api/collection.bru @@ -0,0 +1,34 @@ +docs { + Los endpoint tienen unos campos comunes de entrada: + ```ts + { + iccid: string, + update_webhook?: string + } + ``` + + `update_webhook` está en desarrollo, pero será donde se mande la actualizacion de la cancelación cuando haya una respuesta de la API externa. + + Si la llamada tiene exito devuelve: + ```ts + { + data: { + iccid: string, + message_id: string, + operation: string, + } + } + + ``` + message_id se usará para la llamada /orders/message_id/}{message_id} + + Si la llamada falla devolvera: + ```ts + { + errors: { + msg: string + ... (campos extra de gestion del error) + } + } + ``` +} diff --git a/packages/sim-consumidor-nos/.env b/packages/sim-consumidor-nos/.env index 28a8d8c..6979d6b 100644 --- a/packages/sim-consumidor-nos/.env +++ b/packages/sim-consumidor-nos/.env @@ -3,20 +3,3 @@ 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-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index bfb514e..4ea864d 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -3,6 +3,7 @@ import { SimUsecases } from "./Sim.usecases.js" import { activationValidator, iccidValidator } from "./httpValidators.js" import { companyFromIccid } from "#domain/companies.js" import { BodyValidator } from "sim-shared/aplication/BodyValidator.js" +import { tryCatch } from "packages/sim-shared/domain/Result.js" export class SimController { @@ -37,19 +38,21 @@ export class SimController { const body = req.body // 1. Validacion del body - try { - if (args.validator != undefined) - args.validator.validate(body) - } catch (e) { - if (args.onError != undefined) args.onError(body, e as string) - res.status(422).json({ - errors: { - msg: e - } - }) + if (args.validator != undefined) { + const validationResult = args.validator.validate(body) + if (validationResult.error != undefined) { + res.status(422).json({ + errors: { + ...validationResult.error + } + }) + args.onError(body, validationResult.error.msg) + return 1; + } } // 2. Transformacion del body + // TODO: sustituir el try cach let data: P = body; try { if (args.mapBody != undefined) @@ -60,26 +63,33 @@ export class SimController { msg: "Error parseando el body: " + e } }) + args.onError(body, String(e)) + return 1; } // 3. Aplicacion del UseCase - try { - const usecaseResult = await args.useCase(data) - // 4. Se devuelve al usuario el caso de exito - res.status(200).json( - usecaseResult - ).send() - args.onSuccess(data) - } catch (err) { + // TODO: todos los use cases tienen que pasar a devolver un Result<> + const usecaseResult = await args.useCase(data) // no deberia hacer falta el trycatch + + // 4. Casos de error del usecase + if (usecaseResult.error != undefined) { // 4.1 Error del caso de uso res.status(500).json({ errors: { - msg: "Error general:" + err + ...usecaseResult.error } }).send() - return; + args.onError(body, usecaseResult.error.msg.message) + return 1; } + // 5. Se devuelve al usuario el caso de exito + res.status(200).json( + usecaseResult.data + ).send() + args.onSuccess(usecaseResult.data) + return 0; + } } @@ -92,155 +102,64 @@ export class SimController { console.log("OK", data) } }) - } public preactivation() { - return async (req: Request, res: Response) => { - console.warn("[!] Se deberia de usar la peticion /sim/activate directamente") - try { - iccidValidator.validate(req.body) - } catch (e) { - res.status(422).json({ - errors: { - msg: e - } - }) - } - - const { iccid } = req.body - const compañia = companyFromIccid(iccid) - - try { - await this.simUseCases.preActivation({ iccid, compañia }) - - res.status(200).json({ - iccid: iccid, - operation: "activation" - }).send() - } catch (err) { - console.error("Error activando la sim ", req.body) - res.status(500).json({ - errors: { - msg: "Error general de activation" - } - }).send() - return; - } - } + return this.controllerGenerator<{ iccid: string, offer: string }, { iccid: string, offer: string, compañia: string }>({ + validator: activationValidator, + mapBody: (b) => { + const { iccid, offer } = b + const compañia = companyFromIccid(iccid) + return { iccid, compañia, offer } + }, + useCase: (args) => this.simUseCases.preActivation(args), + onError: (d, e) => console.error("[x] Error preactivation: ", d, e), + onSuccess: console.log + }) } public activation() { - return async (req: Request, res: Response) => { - try { - activationValidator.validate(req.body) - } catch (e) { - res.status(422).json({ - errors: { - msg: e - } - }) - console.error("[!] Error validando mensaje") - return; - } - - const { iccid, offer } = req.body - - const compañia = companyFromIccid(iccid) - - if (compañia == undefined) { - res.status(500).json({ - errors: { - msg: "El iccid no pertenece a una compañia conocida" - } - }) - return; - } - - - try { - await this.simUseCases.activation({ iccid, compañia, offer }) - - res.status(200).json({ - iccid: iccid, - operation: "activation" - }).send() - return; - - } catch (err) { - console.error("Error activando la sim ", req.body) - res.status(500).json({ - errors: { - msg: "Error general de activation" - } - }).send() - return; - } - } + return this.controllerGenerator<{ iccid: string, offer: string }, { iccid: string, offer: string, compañia: string }>({ + validator: activationValidator, + mapBody: (b) => { + const { iccid, offer } = b + const compañia = companyFromIccid(iccid) + return { iccid, compañia, offer } + }, + useCase: (args) => this.simUseCases.activation(args), + onError: (d, e) => console.error("[x] Error activacion: ", d, e), + onSuccess: console.log + }) } public cancelation() { - return async (req: Request, res: Response) => { - try { - iccidValidator.validate(req.body) - } catch (e) { - res.status(422).json({ - errors: { - msg: e - } - }) - } - - const { iccid } = req.body - const compañia = companyFromIccid(iccid) - - try { - await this.simUseCases.cancelation({ iccid, compañia }) - res.status(200).json({ - iccid: iccid, - operation: "cancelation" - }) - } catch (err) { - console.error("Error cancelando la sim ", req.body) - res.status(500).json({ - errors: { - msg: "Error general de cancelacion" - } - }) - } - } + return this.controllerGenerator<{ iccid: string }, { iccid: string, compañia: string }>({ + validator: iccidValidator, + mapBody: (b) => { + const { iccid } = b + const compañia = companyFromIccid(iccid) + return { iccid, compañia } + }, + useCase: (args) => this.simUseCases.cancelation(args), + // TODO: Meter en los mensajes el nombre de la operacion + onError: (d, e) => console.error("[x] Error cancelacion: ", d, e), + onSuccess: console.log + }) } public pause() { - return async (req: Request, res: Response) => { - try { - iccidValidator.validate(req.body) - } catch (e) { - res.status(422).json({ - errors: { - msg: e - } - }) - } + return this.controllerGenerator<{ iccid: string }, { iccid: string, compañia: string }>({ + validator: iccidValidator, + mapBody: (b) => { + const { iccid } = b + const compañia = companyFromIccid(iccid) + return { iccid, compañia } + }, + useCase: (args) => this.simUseCases.pause(args), + onError: (d, e) => console.error("[x] Error pausa: ", d, e), + onSuccess: console.log + }) - const { iccid } = req.body - const compañia = companyFromIccid(iccid) - - try { - await this.simUseCases.pause({ iccid, compañia }) - res.status(200).json({ - iccid: iccid, - operation: "cancelation" - }) - } catch (err) { - console.error("Error pausando la sim ", req.body) - res.status(500).json({ - errors: { - msg: "Error pausando la sim" - } - }) - } - } } public free() { diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index 4e307f9..d1bd2af 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -24,7 +24,7 @@ export class SimUsecases { } /** - * Añade un id de mensaje (correlation_id en ala base de datos) + * Añade un id de mensaje (correlation_id en la base de datos) a los mensajes que van a entrar en la cola */ private addMessage_id(event: SimEvents.general): SimEvents.general & { headers: { message_id: string } } { const uuid = uuidv7() @@ -65,7 +65,6 @@ export class SimUsecases { const result = await this.orderRepository.createOrder(order) return result; - } async test(args: { iccid: string }) { @@ -84,7 +83,7 @@ export class SimUsecases { } /** - * WIP + * TODO: * Crea una nueva sim de la que no se tenia registro anteriormente * Si ya existia se modifican los campos pero no se hace un cambio * de estado. @@ -101,8 +100,8 @@ export class SimUsecases { return this.eventBus.publish([activationEvent]) } - async activation(args: { iccid: string, compañia: string, offer: string }) { - + async activation(args: { iccid: string, compañia: string, offer: string }): + Promise> { const activationEvent = { key: `sim.${args.compañia}.activate`, payload: { @@ -110,14 +109,29 @@ export class SimUsecases { offer: args.offer } } - const activationWithId = this.addMessage_id(activationEvent) console.log("[d] Activation ", activationWithId) await this.eventBus.publish([activationWithId]) - await this.saveOrder(activationWithId) + const createdOrder = await this.saveOrder(activationWithId) + + if (createdOrder.error != undefined) { + console.error(createdOrder.error) + return { + error: createdOrder.error + } + } + + return { + data: { + iccid: args.iccid, + operation: "activation", + message_id: createdOrder.data?.correlation_id + } + } } - async preActivation(args: { iccid: string, compañia: string }) { + async preActivation(args: { iccid: string, compañia: string }): + Promise> { const preActivationEvent = { key: `sim.${args.compañia}.preActivate`, @@ -126,13 +140,30 @@ export class SimUsecases { } } console.log("[d] Pre - activation ", preActivationEvent) - return this.eventBus.publish([preActivationEvent]) + await this.eventBus.publish([preActivationEvent]) + const preactivationWithId = this.addMessage_id(preActivationEvent) + const createdOrder = await this.saveOrder(preactivationWithId) + if (createdOrder.error != undefined) { + console.error(createdOrder.error) + return { + error: createdOrder.error + } + } + + return { + data: { + iccid: args.iccid, + operation: "preactivation", + message_id: createdOrder.data?.correlation_id + } + } } /** * Para objenious es terminate */ - async cancelation(args: { iccid: string, compañia: string }) { + async cancelation(args: { iccid: string, compañia: string }): + Promise> { const cancelationEvent = { key: `sim.${args.compañia}.cancel`, @@ -144,8 +175,21 @@ export class SimUsecases { const cancelationWithId = this.addMessage_id(cancelationEvent) console.log("[d] Cancelation ", cancelationWithId) await this.eventBus.publish([cancelationWithId]) - await this.saveOrder(cancelationWithId) - return cancelationWithId + const savedOrder = await this.saveOrder(cancelationWithId) + if (savedOrder.error != undefined) { + console.error(savedOrder.error) + return { + error: savedOrder.error + } + } + + return { + data: { + iccid: args.iccid, + message_id: savedOrder.data.correlation_id, + operation: "cancelation" + } + } } // alias por si acaso public terminate = this.cancelation; @@ -153,7 +197,8 @@ export class SimUsecases { /** * alias de bloquear / suspender en objenious */ - async pause(args: { iccid: string, compañia: string }) { + async pause(args: { iccid: string, compañia: string }): + Promise> { const pauseEvent = { key: `sim.${args.compañia}.pause`, payload: { @@ -161,10 +206,25 @@ export class SimUsecases { } } const pauseWithId = this.addMessage_id(pauseEvent) - console.log("[d] Cancelation ", pauseWithId) + console.log("[d] Pause", pauseWithId) await this.eventBus.publish([pauseWithId]) await this.saveOrder(pauseWithId) - return pauseWithId + const savedOrder = await this.saveOrder(pauseWithId) + + if (savedOrder.error != undefined) { + console.error(savedOrder.error) + return { + error: savedOrder.error + } + } + + return { + data: { + iccid: args.iccid, + message_id: savedOrder.data.correlation_id, + operation: "cancelation" + } + } } async free(args: { iccid: string, compañia: string }) { diff --git a/packages/sim-entrada-eventos/aplication/httpValidators.test.ts b/packages/sim-entrada-eventos/aplication/httpValidators.test.ts index 15180fd..00ca9f1 100644 --- a/packages/sim-entrada-eventos/aplication/httpValidators.test.ts +++ b/packages/sim-entrada-eventos/aplication/httpValidators.test.ts @@ -8,9 +8,10 @@ describe("test validators", () => { iccid: "8933201125068886692" } const res = iccidValidator.validate(validBody) - assert(res == true) + assert(res.error == undefined) }), + // TODO: Nada de esto es valido, a partir de ahora los validadores no lanzan excepcion sino Result it("shouldnt validate empty string iccid", () => { const validBody = { iccid: "" diff --git a/packages/sim-entrada-eventos/aplication/httpValidators.ts b/packages/sim-entrada-eventos/aplication/httpValidators.ts index 2ab857a..ad566f8 100644 --- a/packages/sim-entrada-eventos/aplication/httpValidators.ts +++ b/packages/sim-entrada-eventos/aplication/httpValidators.ts @@ -3,7 +3,9 @@ import { BodyValidator, Validator } from "sim-shared/aplication/BodyValidator.js const offers = new Map([ ["mensual", "SAVEFAMILY1"], - ["anual", "SAVEFAMILY2"] + ["anual", "SAVEFAMILY2"], + ["SAVEFAMILY1", "SAVEFAMILY1"], + ["SAVEFAMILY2", "SAVEFAMILY2"], ]) const iccidLongitudValidator = >{ diff --git a/packages/sim-objenious-cron/config/env/index.ts b/packages/sim-objenious-cron/config/env/index.ts index a8d2fac..229480e 100644 --- a/packages/sim-objenious-cron/config/env/index.ts +++ b/packages/sim-objenious-cron/config/env/index.ts @@ -46,5 +46,4 @@ if (env.ENVIRONMENT == "production") { assert(env.RABBITMQ_HOST != "localhost") } -console.log("CRON: ENV", env) diff --git a/packages/sim-shared/aplication/BodyValidator.ts b/packages/sim-shared/aplication/BodyValidator.ts index 15ed2b9..5e22570 100644 --- a/packages/sim-shared/aplication/BodyValidator.ts +++ b/packages/sim-shared/aplication/BodyValidator.ts @@ -1,3 +1,5 @@ +import { Result } from "../domain/Result.js" + export type Validator = { field: keyof T, errorMsg: string, @@ -16,10 +18,18 @@ export class BodyValidator { this.validatorList = validators } - public validate(obj: T) { + public validate(obj: T): Result<{ msg: string, field: string }, boolean> { for (const validator of this.validatorList) { - if (validator.validationFunc(obj) == false) throw new Error(validator.errorMsg) + if (validator.validationFunc(obj) == false) + return { + error: { + msg: validator.errorMsg, + field: String(validator.field) + } + } } - return true; + return { + data: true + }; } } diff --git a/packages/sim-shared/domain/Result.ts b/packages/sim-shared/domain/Result.ts index 66e8e4b..a8bda25 100644 --- a/packages/sim-shared/domain/Result.ts +++ b/packages/sim-shared/domain/Result.ts @@ -1,14 +1,31 @@ + +export type Success = { + error?: undefined | null, + data: D +} + +export type Failure = { + data?: undefined | null, + error: E +} + /** * Result */ -export type Result = - { - error: E, - data?: undefined - } - | - { - error?: undefined, - data: D - } +export type Result = Failure | Success + +export async function tryCatch(func: Promise): Promise> { + try { + const res = await func; + return { + data: res + } + } catch (e: unknown) { + return { + error: { + msg: e as Error + } + } + } +}