From 5bb3bc554b5bbd2d4649e211017d99da4da24b92 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 24 Mar 2026 11:20:59 +0100 Subject: [PATCH 01/41] doc --- docs/sim-objenious/Get request by ID.bru | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sim-objenious/Get request by ID.bru b/docs/sim-objenious/Get request by ID.bru index bda2942..f4ebaff 100644 --- a/docs/sim-objenious/Get request by ID.bru +++ b/docs/sim-objenious/Get request by ID.bru @@ -37,7 +37,7 @@ body:form-urlencoded { } vars:pre-request { - params.id: 14557 + params.id: 15102 } settings { From 28880c4d9928c7b3d90f5d08895ddab583a28652 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 24 Mar 2026 17:27:52 +0100 Subject: [PATCH 02/41] Lineas activas e insertar cada una --- .../database/base/xx-volcado-objenious.sql | 20 +++ docs/sim-objenious/Appel lines.bru | 10 +- .../config/intranetPostgresConfig.ts | 20 +++ .../ObjeniousLinesRepository.ts | 73 +++++++++++ .../tasks/check_objenious_request.ts | 1 + .../tasks/volcado_lineas.ts | 68 +++++++++++ packages/sim-shared/domain/objeniousLine.ts | 115 ++++++++++++++++++ 7 files changed, 302 insertions(+), 5 deletions(-) create mode 100644 deployment/database/base/xx-volcado-objenious.sql create mode 100644 packages/sim-objenious-cron/config/intranetPostgresConfig.ts create mode 100644 packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts create mode 100644 packages/sim-objenious-cron/tasks/volcado_lineas.ts create mode 100644 packages/sim-shared/domain/objeniousLine.ts diff --git a/deployment/database/base/xx-volcado-objenious.sql b/deployment/database/base/xx-volcado-objenious.sql new file mode 100644 index 0000000..8637c89 --- /dev/null +++ b/deployment/database/base/xx-volcado-objenious.sql @@ -0,0 +1,20 @@ +CREATE table if not exists objenious_lines ( + id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + simId BIGINT UNIQUE, + status TEXT, + iccid TEXT NOT NULL, + msisdn TEXT, + imei TEXT, + imeiChangeDate TIMESTAMPTZ, + offerCode TEXT, + status TEXT, + preactivationDate TIMESTAMPTZ, -- No viene con hora + activationDate TIMESTAMPTZ, + commercialStatus TEXT, + commercialStatusDate TIMESTAMPTZ, + billingStatus TEXT, + billingStatusChangeDate TIMESTAMPTZ, + billingActivationDate TIMESTAMPTZ, + createDate TIMESTAMPTZ, + raw JSONB +) diff --git a/docs/sim-objenious/Appel lines.bru b/docs/sim-objenious/Appel lines.bru index e10670f..afc428d 100644 --- a/docs/sim-objenious/Appel lines.bru +++ b/docs/sim-objenious/Appel lines.bru @@ -5,16 +5,16 @@ meta { } get { - url: https://api-getway.objenious.com/ws/lines?pageSize=10&identifier.identifierType=ICCID&identifier.identifiers=8933201125065160455 + url: https://api-getway.objenious.com/ws/lines?pageSize=1000&simStatus=ACTIVATED body: formUrlEncoded auth: bearer } params:query { - pageSize: 10 - identifier.identifierType: ICCID - identifier.identifiers: 8933201125065160455 - ~simStatus: ACTIVATED + pageSize: 1000 + simStatus: ACTIVATED + ~identifier.identifierType: ICCID + ~identifier.identifiers: 8933201125065160455 } auth:bearer { diff --git a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts new file mode 100644 index 0000000..a9d94ee --- /dev/null +++ b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts @@ -0,0 +1,20 @@ +/** + * Cliente de postgres para la intranet. Se usa solo porque hace falta para el + * volcado de datos, si se usa en mas partes algo estás haciendo mal. + */ + +import { Pool } from 'pg'; +import { PgClient } from 'sim-shared/infrastructure/PgClient.js' +import { env } from './env/index.js'; + +export const pgPoolIntranet = new Pool({ + user: env.POSTGRES_USER, + host: env.POSTGRES_HOST, + database: "intranet", + password: env.POSTGRES_PASSWORD, + port: Number(env.POSTGRES_PORT) || 5432, +}); + +export const postgrClientIntranet = new PgClient({ + pool: pgPoolIntranet +}) diff --git a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts new file mode 100644 index 0000000..3bafa70 --- /dev/null +++ b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts @@ -0,0 +1,73 @@ +import { PoolClient } from "pg"; +import { CreateObjeniousLineDTO } from "sim-shared/domain/objeniousLine.js"; +import { PgClient } from "sim-shared/infrastructure/PgClient.js"; + +export class ObjeniousLinesRepository { + constructor( + private pgClient: PgClient + ) { + } + + public async insertOrUpdate(data: CreateObjeniousLineDTO) { + const query = ` + INSERT INTO objenious_lines ( + simId, iccid, msisdn, imei, imeiChangeDate, + offerCode, status, preactivationDate, activationDate, + commercialStatus, commercialStatusDate, billingStatus, + billingStatusChangeDate, billingActivationDate, createDate, raw + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 + ) + ON CONFLICT (simId) + DO UPDATE SET + iccid = EXCLUDED.iccid, + msisdn = EXCLUDED.msisdn, + imei = EXCLUDED.imei, + imeiChangeDate = EXCLUDED.imeiChangeDate, + offerCode = EXCLUDED.offerCode, + status = EXCLUDED.status, + preactivationDate = EXCLUDED.preactivationDate, + activationDate = EXCLUDED.activationDate, + commercialStatus = EXCLUDED.commercialStatus, + commercialStatusDate = EXCLUDED.commercialStatusDate, + billingStatus = EXCLUDED.billingStatus, + billingStatusChangeDate = EXCLUDED.billingStatusChangeDate, + billingActivationDate = EXCLUDED.billingActivationDate, + raw = EXCLUDED.raw + RETURNING id; + `; + + const values = [ + data.simId, + data.iccid, + data.msisdn, + data.imei, + data.imeiChangeDate, + data.offerCode, + data.status, + data.preactivationDate, + data.activationDate, + data.commercialStatus, + data.commercialStatusDate, + data.billingStatus, + data.billingStatusChangeDate, + data.billingActivationDate, + data.createDate || new Date(), // Default a ahora si no viene + JSON.stringify(data.raw) // El driver de pg requiere string o el objeto directo para JSONB + ]; + + let client: PoolClient | undefined = undefined; + try { + client = await this.pgClient.connect(); + const res = await client.query(query, values); + return res.rows[0].id; + } catch (err) { + console.error('Error en la inserción:', err); + throw err; + } finally { + if (client != undefined) { + client.release() + } + } + } +} diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index 49c1f2a..5b85fbe 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -54,6 +54,7 @@ export class CheckObjeniousRequests { /** * Para una lista de operaciones **con mass_action_id** se comprueba si han tenido alguna actualizacion * Devuelve el numero de operaciones comprobadas. + * TODO: Esto va en un repositorio */ private async getMassActionsStatus(requestList: ObjeniousOperation[]) { if (requestList.length == 0) return 0; diff --git a/packages/sim-objenious-cron/tasks/volcado_lineas.ts b/packages/sim-objenious-cron/tasks/volcado_lineas.ts new file mode 100644 index 0000000..1155702 --- /dev/null +++ b/packages/sim-objenious-cron/tasks/volcado_lineas.ts @@ -0,0 +1,68 @@ +import { ObjeniousLine, ObjeniousLineResponse } from "sim-shared/domain/objeniousLine.js"; +import { tryCatch, Result } from "sim-shared/domain/Result.js"; +import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"; + +const MAX_PAGE_SIZE = 1000 + +export class VolcadoLineas { + constructor( + private readonly httpClient: HttpClient + ) { + } + + /** + * Mover al repo + */ + private async getLinesByStatus(args?: { + pageSize?: number, + pageNumber?: number, + status?: string + }): Promise> { + + const path = "/lines" + const pageSize = args?.pageSize ?? MAX_PAGE_SIZE; + const status = args?.status ?? null; + let currentPage = args?.pageNumber ?? 0; + let totalPages = 1; + let allLines: ObjeniousLine[] = [] + + const loadNextLine = async () => { + const nextPage = await tryCatch(this.httpClient.client.get(path, { + params: { + simStatus: status, + pageSize: pageSize, + pageNumber: currentPage + } + })) + if (nextPage.error != undefined) { + return { + error: nextPage.error.msg.message + } + } + + // Se aumenta para la siguiente ejecucion + currentPage = nextPage.data.pageNumber + 1 + allLines = [...allLines, ...nextPage.data.content] + totalPages = nextPage.data.totalPages + } + + // El inicio se ejecuta siempre + await loadNextLine() + + // Copia para evitar bucles infinitos por error de la api + const maxPages = totalPages + + for (let i = currentPage; i < maxPages; i++) { + await loadNextLine() + } + + return { + data: allLines + } + } + + public async loadLines() { + + } + +} diff --git a/packages/sim-shared/domain/objeniousLine.ts b/packages/sim-shared/domain/objeniousLine.ts new file mode 100644 index 0000000..aa763fb --- /dev/null +++ b/packages/sim-shared/domain/objeniousLine.ts @@ -0,0 +1,115 @@ +export type ObjeniousLineResponse = { + content: ObjeniousLine[], + offset: number, + pageNumber: number, + pageSize: number, + paged: boolean, + totalPages: number, + totalElements: number +} + +export type ObjeniousLine = { + identifier: { + simId: number, + iccid: string, + imsi: string, + msisdn: string, + amsisdn?: string, + imei: string + }, + simCardType: { + code: string, + description: string + }, + device: { + imei: string, + imeiChangeDate: string, //Fecha iso + deviceReference?: string | null, + manufacturer?: string | null, + }, + customerAccount: { + code: string, + label: string, + address: { + address1: string, + address2: string, + address3: string, + zipCode: string, + city: string, + country: string, + state?: string | null + } + }, + offer: { + code: string, + description: string, + }, + party: { + name: string, + code: string, + contractReference: string, + partyType: string, + }, + lineCustomFields: { + custom1: { + label: string | null, + value: string | null + }, + custom2: { + label: string | null, + value: string | null + }, + custom3: { + label: string | null, + value: string | null + }, + custom4: { + label: string | null, + value: string | null + }, + custom5: { + label: string | null, + value: string | null + }, + custom6: { + label: string | null, + value: string | null + } + }, + status: { + status: string, + preactivationDate: string | null, //"2026-03-17", + activationDate: string | null, //"2026-03-17T11:04:11.408+00:00", + commercialStatus: string, //"test", + commercialStatusDate: string, //"2026-03-17T11:41:01.493+00:00", + networkStatus: string, // "ACTIVATED", + billingStatus: string, //"TEST", + billingStatusChangeDate: string | null, // "2026-03-17T11:01:00.276+00:00", + billingActivationDate: string | null //, + createdDate: string | null,//"2026-01-30T01:50:02.060+00:00" + }, + services: string | null +}; + +export type ObjeniousLineDb = { + id: number; + simId?: string; + iccid: string; + msisdn?: string; + imei?: string; + imeiChangeDate?: Date; + offerCode?: string; + status?: string; + preactivationDate?: Date; + activationDate?: Date; + commercialStatus?: string; + commercialStatusDate?: Date; + billingStatus?: string; + billingStatusChangeDate?: Date; + billingActivationDate?: Date; + createDate?: Date; + raw: ObjeniousLine; +} + +// DTO para inserción (omite el ID autogenerado) +export type CreateObjeniousLineDTO = Omit; From 025801a689ed6cf7146786766c081451012a8230 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 25 Mar 2026 11:51:14 +0100 Subject: [PATCH 03/41] Repositorio de lineas funciona --- .../database/base/xx-volcado-objenious.sql | 4 +- .../config/intranetPostgresConfig.ts | 2 +- .../ObjeniousLinesRepository.test.ts | 40 +++++++++++++++ .../ObjeniousLinesRepository.ts | 51 +++++++++++++++---- packages/sim-objenious-cron/package.json | 16 +----- 5 files changed, 86 insertions(+), 27 deletions(-) create mode 100644 packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts diff --git a/deployment/database/base/xx-volcado-objenious.sql b/deployment/database/base/xx-volcado-objenious.sql index 8637c89..4417b5c 100644 --- a/deployment/database/base/xx-volcado-objenious.sql +++ b/deployment/database/base/xx-volcado-objenious.sql @@ -7,7 +7,6 @@ CREATE table if not exists objenious_lines ( imei TEXT, imeiChangeDate TIMESTAMPTZ, offerCode TEXT, - status TEXT, preactivationDate TIMESTAMPTZ, -- No viene con hora activationDate TIMESTAMPTZ, commercialStatus TEXT, @@ -16,5 +15,6 @@ CREATE table if not exists objenious_lines ( billingStatusChangeDate TIMESTAMPTZ, billingActivationDate TIMESTAMPTZ, createDate TIMESTAMPTZ, - raw JSONB + raw JSONB, + hash TEXT ) diff --git a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts index a9d94ee..7797136 100644 --- a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts +++ b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts @@ -15,6 +15,6 @@ export const pgPoolIntranet = new Pool({ port: Number(env.POSTGRES_PORT) || 5432, }); -export const postgrClientIntranet = new PgClient({ +export const postgresClientIntranet = new PgClient({ pool: pgPoolIntranet }) diff --git a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts new file mode 100644 index 0000000..83a9f80 --- /dev/null +++ b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts @@ -0,0 +1,40 @@ +import test, { describe } from "node:test"; +import { CreateObjeniousLineDTO } from "sim-shared/domain/objeniousLine.js"; +import { ObjeniousLinesRepository } from "./ObjeniousLinesRepository.js"; +import { postgrClient } from "../config/postgreConfig.js"; +import assert from "node:assert"; + +describe("Line insertion test", async () => { + //const pgClient = postgreClientIntranet + const pgClient = postgrClient // En prod hay que usar el de Intrantet para usar la otra base de datos + const lineRepository = new ObjeniousLinesRepository(pgClient) + const lineaTest: CreateObjeniousLineDTO = { + simId: "1234", + iccid: "9999999999999", + msisdn: "34654674732", + imei: "219789481293", + imeiChangeDate: new Date(), + offerCode: "SAVEFAMILY1", + status: "ACTIVATED", + preactivationDate: new Date(), + activationDate: new Date(), + commercialStatus: "test", + commercialStatusDate: new Date(), + billingStatus: "test", + billingStatusChangeDate: new Date(), + billingActivationDate: new Date(), + createDate: new Date(), + raw: { test: "test" } as any // Para este test no hace falta + } + + test("Should insert new line", async () => { + const res = await lineRepository.insertOrUpdate(lineaTest) + assert.ok(res != undefined, "The line wasn't created") + }) + + test("Should not update a line if the hash is the same", async () => { + const res = await lineRepository.insertOrUpdate(lineaTest) + console.log("Update", res) + assert.ok(res != undefined, "The line wasn't updated") + }) +}) diff --git a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts index 3bafa70..7499db6 100644 --- a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts +++ b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts @@ -8,15 +8,38 @@ export class ObjeniousLinesRepository { ) { } + private generateLineHash(data: CreateObjeniousLineDTO) { + try { + const lineStr = JSON.stringify(data) + return lineStr + } catch (e) { + console.error("[x] Error generando el hash de la linea", data) + return undefined + } + } + public async insertOrUpdate(data: CreateObjeniousLineDTO) { const query = ` INSERT INTO objenious_lines ( - simId, iccid, msisdn, imei, imeiChangeDate, - offerCode, status, preactivationDate, activationDate, - commercialStatus, commercialStatusDate, billingStatus, - billingStatusChangeDate, billingActivationDate, createDate, raw + simId, + iccid, + msisdn, + imei, + imeiChangeDate, + offerCode, + status, + preactivationDate, + activationDate, + commercialStatus, + commercialStatusDate, + billingStatus, + billingStatusChangeDate, + billingActivationDate, + createDate, + raw, + hash ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17 ) ON CONFLICT (simId) DO UPDATE SET @@ -33,10 +56,19 @@ export class ObjeniousLinesRepository { billingStatus = EXCLUDED.billingStatus, billingStatusChangeDate = EXCLUDED.billingStatusChangeDate, billingActivationDate = EXCLUDED.billingActivationDate, - raw = EXCLUDED.raw + raw = EXCLUDED.raw, + hash = EXCLUDED.hash + WHERE objenious_lines.hash IS DISTINCT FROM EXCLUDED.hash RETURNING id; `; + const lineHash = this.generateLineHash(data) + + if (lineHash == undefined) { + console.error("[x] Ignorando linea ", data) + return; + } + const values = [ data.simId, data.iccid, @@ -53,14 +85,15 @@ export class ObjeniousLinesRepository { data.billingStatusChangeDate, data.billingActivationDate, data.createDate || new Date(), // Default a ahora si no viene - JSON.stringify(data.raw) // El driver de pg requiere string o el objeto directo para JSONB + JSON.stringify(data.raw), // El driver de pg requiere string o el objeto directo para JSONB + lineHash ]; let client: PoolClient | undefined = undefined; try { client = await this.pgClient.connect(); - const res = await client.query(query, values); - return res.rows[0].id; + const res = await client.query<{ id: number }>(query, values); + return res.rows[0]; } catch (err) { console.error('Error en la inserción:', err); throw err; diff --git a/packages/sim-objenious-cron/package.json b/packages/sim-objenious-cron/package.json index bf8da38..af8ef70 100644 --- a/packages/sim-objenious-cron/package.json +++ b/packages/sim-objenious-cron/package.json @@ -5,20 +5,6 @@ "description": "", "main": "index.ts", "imports": { - "#config/*.js": { - "types": "./config/*.ts", - "default": "./config/*.js" - }, - "#config/*": { - "types": "./config/*.ts", - "default": "./config/*.js" - }, - "#shared/*.js": { - "default": "../sim-shared/*.js" - }, - "#shared/*": { - "default": "../sim-shared/*.js" - }, "#adapters/*.js": { "types": "./infrastructure/*.ts", "default": "./infrastructure/*.js" @@ -45,7 +31,7 @@ } }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "node --import tsx --test ./**/*.test.ts", "build": "tsc --build && tsc-alias -p tsconfig.json && cp package.json ../../dist/packages/sim-objenious-cron/", "dev": "tsx watch index.ts", "start": "node ../../dist/packages/sim-objenious-cron/index.js" From a95655a2a604a6662767863081bfdba87e7e606a Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 26 Mar 2026 09:29:09 +0100 Subject: [PATCH 04/41] Completada la tarea de volcado --- .../config/intranetPostgresConfig.ts | 2 +- packages/sim-objenious-cron/index.ts | 41 ++++--- .../ObjeniousLinesRepository.test.ts | 27 +++- .../ObjeniousLinesRepository.ts | 8 +- .../tasks/check_objenious_request.ts | 5 +- .../tasks/volcado_lineas.ts | 115 ++++++++++++++---- packages/sim-shared/domain/objeniousLine.ts | 43 +++++-- 7 files changed, 187 insertions(+), 54 deletions(-) diff --git a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts index 7797136..001134b 100644 --- a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts +++ b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts @@ -10,7 +10,7 @@ import { env } from './env/index.js'; export const pgPoolIntranet = new Pool({ user: env.POSTGRES_USER, host: env.POSTGRES_HOST, - database: "intranet", + database: "postgres", password: env.POSTGRES_PASSWORD, port: Number(env.POSTGRES_PORT) || 5432, }); diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index e9cf2ef..38a83f6 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -5,6 +5,9 @@ import { httpInstance } from "./config/httpClient.config.js" import { CheckObjeniousRequests } from "./tasks/check_objenious_request.js" import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js" import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js" +import { TaskVolcadoLineas } from "./tasks/volcado_lineas.js" +import { ObjeniousLinesRepository } from "./infranstructure/ObjeniousLinesRepository.js" +import { postgresClientIntranet } from "./config/intranetPostgresConfig.js" async function startCron() { const commonSettings = { @@ -14,10 +17,13 @@ async function startCron() { const httpClient = httpInstance const pgClient = new PgClient({ pool: pgPool }) + + console.log("[i] Comprobando conexion con la BDD ") await pgClient.checkDatabaseConnection() - await pgClient.checkDatabaseConnection() + const operationRepository = new ObjeniousOperationsRepository(pgClient) const orderRepository = new OrderRepository(pgClient) + const objeniousLineRepository = new ObjeniousLinesRepository(postgresClientIntranet) const objTask = new CheckObjeniousRequests( operationRepository, @@ -25,23 +31,28 @@ async function startCron() { httpClient, ) - await objTask.getPendingOperations() + const volcadoLineasTask = new TaskVolcadoLineas(httpClient, objeniousLineRepository) + const PERIODO_PETICIONES = 10 * 60 * 60 const interval = setInterval(async () => { - console.log("Updating...") - await objTask.getPendingOperations() - console.log("Update finished") - }, 10 * 60 * 1000) - /* - const task = cron.createTask("* * * * *", async () => { - } - , { - ...commonSettings, - name: "Test" - }) -*/ + try { + await objTask.getPendingOperations() + } catch (e) { + console.error("[x] Error de actualizacion de las lineas ") + } + }, PERIODO_PETICIONES) + + const PERIODO_VOLCADO = 60 * 60 * 1000 + const volcadoInterval = setInterval(async () => { + try { + await volcadoLineasTask.loadLines() + } catch (e) { + console.error("[x] Volcado de lineas de Objenious Fallido", e) + } + }, PERIODO_VOLCADO) + + await volcadoLineasTask.loadLines() - //await objTask.getPendingOperations() } diff --git a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts index 83a9f80..7fa5623 100644 --- a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts +++ b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.test.ts @@ -1,4 +1,4 @@ -import test, { describe } from "node:test"; +import test, { after, before, describe } from "node:test"; import { CreateObjeniousLineDTO } from "sim-shared/domain/objeniousLine.js"; import { ObjeniousLinesRepository } from "./ObjeniousLinesRepository.js"; import { postgrClient } from "../config/postgreConfig.js"; @@ -9,7 +9,7 @@ describe("Line insertion test", async () => { const pgClient = postgrClient // En prod hay que usar el de Intrantet para usar la otra base de datos const lineRepository = new ObjeniousLinesRepository(pgClient) const lineaTest: CreateObjeniousLineDTO = { - simId: "1234", + simId: 1234, iccid: "9999999999999", msisdn: "34654674732", imei: "219789481293", @@ -27,6 +27,19 @@ describe("Line insertion test", async () => { raw: { test: "test" } as any // Para este test no hace falta } + // Clean up before and after tests to ensure isolation + const cleanup = async () => { + await pgClient.query("DELETE FROM objenious_lines WHERE simId = 1234"); + }; + + before(async () => { + await cleanup() + }) + + after(async () => { + await cleanup() + }) + test("Should insert new line", async () => { const res = await lineRepository.insertOrUpdate(lineaTest) assert.ok(res != undefined, "The line wasn't created") @@ -34,7 +47,13 @@ describe("Line insertion test", async () => { test("Should not update a line if the hash is the same", async () => { const res = await lineRepository.insertOrUpdate(lineaTest) - console.log("Update", res) - assert.ok(res != undefined, "The line wasn't updated") + assert.ok(res == undefined, "The line have been updated") + }) + + test("Should update a line if the hash changes", async () => { + const updated = structuredClone(lineaTest) + lineaTest.billingActivationDate = new Date() + const res = await lineRepository.insertOrUpdate(lineaTest) + assert.ok(res != undefined, "The line have been updated") }) }) diff --git a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts index 7499db6..4bba217 100644 --- a/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts +++ b/packages/sim-objenious-cron/infranstructure/ObjeniousLinesRepository.ts @@ -1,3 +1,8 @@ +/** + * Repositorio para el volcado de lineas de objenious en intranet + * solo para uso en el volcado. + */ +import { createHash } from "node:crypto"; import { PoolClient } from "pg"; import { CreateObjeniousLineDTO } from "sim-shared/domain/objeniousLine.js"; import { PgClient } from "sim-shared/infrastructure/PgClient.js"; @@ -11,7 +16,8 @@ export class ObjeniousLinesRepository { private generateLineHash(data: CreateObjeniousLineDTO) { try { const lineStr = JSON.stringify(data) - return lineStr + const hash = createHash("sha256").update(lineStr).digest("base64url") + return hash } catch (e) { console.error("[x] Error generando el hash de la linea", data) return undefined diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index 5b85fbe..8e4d5f4 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -1,4 +1,4 @@ -import { env } from "#config/env/index.js"; +import { env } from "../config/env/index.js"; import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"; import axios from "axios"; import { IOperationsRepository, Objenious, ObjeniousOperation, ObjeniousOperationChange, StatusEnum } from "sim-shared/domain/operationsRepository.port.js"; @@ -16,6 +16,7 @@ export class CheckObjeniousRequests { * TODO: meter a una funcion a parte task con los 3 pasos */ public async getPendingOperations() { + console.log("[i] Inicio revision de peticiones") // 1. Se obtienen todas las operaciones pendientes de la BDD const pendingOperations = await this.operationsRepository.getPendingOperations() @@ -49,6 +50,8 @@ export class CheckObjeniousRequests { console.log("[cron] Solicitando status para", merged.map(e => e.id)) const result = await this.getMassActionsStatus(merged) + + console.log("[o] Revisión de eventos completa") } /** diff --git a/packages/sim-objenious-cron/tasks/volcado_lineas.ts b/packages/sim-objenious-cron/tasks/volcado_lineas.ts index 1155702..e8cbd8a 100644 --- a/packages/sim-objenious-cron/tasks/volcado_lineas.ts +++ b/packages/sim-objenious-cron/tasks/volcado_lineas.ts @@ -1,68 +1,133 @@ -import { ObjeniousLine, ObjeniousLineResponse } from "sim-shared/domain/objeniousLine.js"; +import assert from "node:assert"; +import { lineToCreateLineDto, ObjeniousLine, ObjeniousLineResponse } from "sim-shared/domain/objeniousLine.js"; import { tryCatch, Result } from "sim-shared/domain/Result.js"; import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"; +import { ObjeniousLinesRepository } from "../infranstructure/ObjeniousLinesRepository.js"; +import { AxiosResponse } from "axios"; +import { constants } from "node:buffer"; -const MAX_PAGE_SIZE = 1000 +const MAX_PAGE_SIZE = 100 -export class VolcadoLineas { +export class TaskVolcadoLineas { constructor( - private readonly httpClient: HttpClient + private readonly httpClient: HttpClient, + private readonly linesRepository: ObjeniousLinesRepository, ) { } /** * Mover al repo */ - private async getLinesByStatus(args?: { + private async * getLinesByStatus(args?: { pageSize?: number, pageNumber?: number, status?: string - }): Promise> { + }): AsyncGenerator, Result, any> { const path = "/lines" const pageSize = args?.pageSize ?? MAX_PAGE_SIZE; - const status = args?.status ?? null; - let currentPage = args?.pageNumber ?? 0; - let totalPages = 1; - let allLines: ObjeniousLine[] = [] - const loadNextLine = async () => { - const nextPage = await tryCatch(this.httpClient.client.get(path, { - params: { - simStatus: status, - pageSize: pageSize, - pageNumber: currentPage - } + let currentPage = args?.pageNumber ?? 0; + let totalPages: number | undefined = undefined; // Como limite de paginas, igual es pasarse pero hasta que se lea + + const params: Record = {} + + const loadNextLine = async (page: number): Promise> => { + if (args?.status != undefined) params["simStatus"] = args.status + params["pageSize"] = pageSize + params["pageNumber"] = page + console.log("Params", params) + console.log(`[i] Cargando pagina ${currentPage} de ${totalPages ?? "(desc)"}`) + const nextPage = await tryCatch>(this.httpClient.client.get(path, { + params: params })) + if (nextPage.error != undefined) { + console.error(nextPage.error.msg) return { error: nextPage.error.msg.message } } // Se aumenta para la siguiente ejecucion - currentPage = nextPage.data.pageNumber + 1 - allLines = [...allLines, ...nextPage.data.content] - totalPages = nextPage.data.totalPages + console.log(`[i] Página ${currentPage} completa, total: ${nextPage.data.data.totalPages}`) + totalPages = nextPage.data.data.totalPages + + return { + data: nextPage.data.data.content + } + } // El inicio se ejecuta siempre - await loadNextLine() + const lines = await loadNextLine(currentPage) + + if (lines.error != undefined) { + console.error("[x] Error obteniendo las lineas, cancelando operación"); + return { + error: "Error cargando lineas" + } + } + + currentPage++; + + yield { + data: lines.data + } // Copia para evitar bucles infinitos por error de la api const maxPages = totalPages - - for (let i = currentPage; i < maxPages; i++) { - await loadNextLine() + assert.ok(maxPages != undefined, "No se ha defindo el numero de paginas") // Nunca deberia pasar pero así se evitan bucles infnitos + console.log("maxPages", maxPages) + for (let i = currentPage; i < maxPages!; i++) { + console.log("Bucle i:", i, "page: ", currentPage) + yield await loadNextLine(currentPage); + currentPage++; } return { - data: allLines + data: [] + } + } + + private async saveLines(lines: ObjeniousLine[]) { + const linesToCreate = lines.map(lineToCreateLineDto) + let created: number[] = [] + + + for (const line of linesToCreate) { + // Si es lento pasar a Promise.all + const res = await this.linesRepository.insertOrUpdate(line) + if (res?.id != undefined) + created.push(res.id) } } public async loadLines() { + console.log("[i] Iniciando task de volcado de lineas de Objenious") + // Carga todas las lineas en memoria, hay que comprobar que no se gaste demasiada + const linesIterator = this.getLinesByStatus() + let lines = await linesIterator.next() + + if (lines.value.error != undefined || lines.value.data == undefined) { + console.error("[x] Error cargando las lineas a volcar", lines.value.error) + return; + } + + await this.saveLines(lines.value.data) + + while (!lines.done) { + console.log() + lines = await linesIterator.next() + if (lines.value.error != undefined || lines.value.data == undefined) { + console.error("[x] Error cargando las lineas a volcar", lines.value.error) + return; + } + await this.saveLines(lines.value.data) + } + + console.log("[i] Terminado task de volcado de lineas de Objenious") } } diff --git a/packages/sim-shared/domain/objeniousLine.ts b/packages/sim-shared/domain/objeniousLine.ts index aa763fb..c04eb30 100644 --- a/packages/sim-shared/domain/objeniousLine.ts +++ b/packages/sim-shared/domain/objeniousLine.ts @@ -93,23 +93,52 @@ export type ObjeniousLine = { export type ObjeniousLineDb = { id: number; - simId?: string; + simId?: number; iccid: string; msisdn?: string; imei?: string; imeiChangeDate?: Date; offerCode?: string; status?: string; - preactivationDate?: Date; - activationDate?: Date; + preactivationDate?: Date | null; + activationDate?: Date | null; commercialStatus?: string; - commercialStatusDate?: Date; + commercialStatusDate?: Date | null; billingStatus?: string; - billingStatusChangeDate?: Date; - billingActivationDate?: Date; - createDate?: Date; + billingStatusChangeDate?: Date | null; + billingActivationDate?: Date | null; + createDate?: Date | null; raw: ObjeniousLine; } // DTO para inserción (omite el ID autogenerado) export type CreateObjeniousLineDTO = Omit; + +export function lineToCreateLineDto(line: ObjeniousLine): CreateObjeniousLineDTO { + + const dateOrNull = (data: string | null) => { + if (data == null) return null; + return new Date(data) + } + + const transformed: CreateObjeniousLineDTO = { + simId: line.identifier.simId, + iccid: line.identifier.iccid, + msisdn: line.identifier.msisdn, + imei: line.identifier.imei, + imeiChangeDate: new Date(line.device.imeiChangeDate), + offerCode: line.offer.code, + status: line.status.status, + preactivationDate: dateOrNull(line.status.preactivationDate), + activationDate: dateOrNull(line.status.activationDate), + commercialStatus: line.status.commercialStatus, + commercialStatusDate: dateOrNull(line.status.commercialStatusDate), + billingStatus: line.status.billingStatus, + billingStatusChangeDate: dateOrNull(line.status.activationDate), + billingActivationDate: dateOrNull(line.status.activationDate), + createDate: dateOrNull(line.status.activationDate), + raw: line + } + + return transformed; +} From 9d63d2375432ef6b4b8628cd7210c3c8bd08151a Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 26 Mar 2026 12:21:28 +0100 Subject: [PATCH 05/41] Mejor gestion de errores para los order --- .../config/intranetPostgresConfig.ts | 2 +- .../tasks/check_objenious_request.ts | 62 ++++++++++++++++--- packages/sim-shared/domain/Order.ts | 21 +++++-- .../domain/operationsRepository.port.ts | 28 ++++++++- .../infrastructure/OrderRepository.ts | 20 +++--- 5 files changed, 105 insertions(+), 28 deletions(-) diff --git a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts index 001134b..7797136 100644 --- a/packages/sim-objenious-cron/config/intranetPostgresConfig.ts +++ b/packages/sim-objenious-cron/config/intranetPostgresConfig.ts @@ -10,7 +10,7 @@ import { env } from './env/index.js'; export const pgPoolIntranet = new Pool({ user: env.POSTGRES_USER, host: env.POSTGRES_HOST, - database: "postgres", + database: "intranet", password: env.POSTGRES_PASSWORD, port: Number(env.POSTGRES_PORT) || 5432, }); diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index 8e4d5f4..1d158dd 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -123,9 +123,6 @@ export class CheckObjeniousRequests { if (uorStatus == "finished") { console.log(" ****> Status", uorStatus) - if (uorStatus != "finished") { - console.error("!!! Notificando estado no finished") - } const targetIccids = originalAction.iccids const lineData = await this.getLineData(targetIccids) console.log("[i] lineData", lineData.content[0]) @@ -219,7 +216,7 @@ export class CheckObjeniousRequests { const PATH = "/actions/requests/" const operationsList = structuredClone(requestList) - + // TODO: El for es gigantesco hay que simplificar partes for (const request of operationsList) { if (request.id == undefined) continue; @@ -232,13 +229,50 @@ export class CheckObjeniousRequests { try { res = await req } catch (e) { - console.error("Error comprobando el estado de ", request, e) - //todo actualizar el estado para incluir el error + console.error("[x] Error comprobando el estado de ", request, e) + continue; + } + + // 2. Casos de error o id no generada + if (res.data.massActionIds.length == 0) { + // Si no hay es que *puede* que haya un problema o no se ha generado todavia + const reports = res.data.actionRequestReports + // Se entiende que no hay report ni id = está a la espera + if (reports.length == 0) continue; + + // ! Hay minimo un report -> se considera error y se para + const updateData: ObjeniousOperationChange = { + operation_id: request.id, + new_status: "error", + error: JSON.stringify(reports[0].actionRequestReportDataDTOs) + } + + const updateRes = await this.operationsRepository.updateOperation(updateData) + if (updateRes.error != undefined) { + console.error("[x] Error actualizando el estado de la operacion", updateData.error) + } + + if (request.correlation_id != undefined) { + this.orderRepository.errorOrder({ + correlation_id: request.correlation_id, + status: "failed", + error: "MassId no obtenida", + reason: "MassId no obtenida", + stackTrace: JSON.stringify(reports[0].actionRequestReportDataDTOs) + }).then(e => { + if (e.error != undefined) { + console.error("[x] Error actualizando el estado del Order con correlation_id: ", request.correlation_id) + console.error(e.error) + } + }).catch(e => { + console.error("[x] Error actualizando el estado del Order con correlation_id: ", request.correlation_id) + }) + } continue; } - // 2. Modificacion del massId si ha habido un cambio const massActionId = res.data.massActionIds[0] + // 3. Modificacion del massId si ha habido un cambio try { if (res.status == 200 && res.data != undefined && massActionId != undefined) { const updateData: ObjeniousOperationChange = { @@ -252,7 +286,7 @@ export class CheckObjeniousRequests { request.mass_action_id = String(massActionId) } } catch (e) { - console.log("Error actualizando el estado de ", request) + console.log("[x] Error actualizando el estado de ", request) continue; } } @@ -274,7 +308,17 @@ export class CheckObjeniousRequests { "x-apikey-sim-activation": env.SIM_ACTIVATION_API_KEY } }) - await req + try { + const res = await req + if (res.status != 200) { + console.error("[x] Error enviando el mail de confirmacion para ", operation, " status ", res.status, res.statusText) + } + } catch (e) { + console.error("[x] Error enviando el mail de confirmacion para ", operation) + console.error(e) + } + + } } diff --git a/packages/sim-shared/domain/Order.ts b/packages/sim-shared/domain/Order.ts index 30ec573..7f3d35b 100644 --- a/packages/sim-shared/domain/Order.ts +++ b/packages/sim-shared/domain/Order.ts @@ -62,11 +62,14 @@ export type CreateOrderDTO = Pick< 'correlation_id' | 'exchange' | 'routing_key' | 'order_type' | 'payload' | 'webhook_host' | 'webhook_endpoint' >; -export type UpdateOrderDTO = +type IdOrCorrelationID = ( { id: number, correlation_id?: never } | { id?: never, correlation_id: string } ) + +export type UpdateOrderDTO = + IdOrCorrelationID & { new_status: OrderStatus, @@ -74,12 +77,20 @@ export type UpdateOrderDTO = } export type FinishOrderDTO = - ( - { id: number, correlation_id?: never } | - { id?: never, correlation_id: string } - ) + IdOrCorrelationID & { reason?: string } +export type ErrorOrderDTO = + IdOrCorrelationID + & + { + status: "failed" | "dlx", + reason: string, + error?: string, + stackTrace?: string + } + + diff --git a/packages/sim-shared/domain/operationsRepository.port.ts b/packages/sim-shared/domain/operationsRepository.port.ts index 7af02c2..55d694f 100644 --- a/packages/sim-shared/domain/operationsRepository.port.ts +++ b/packages/sim-shared/domain/operationsRepository.port.ts @@ -46,10 +46,34 @@ export namespace Objenious { created: string, status: "NEW" | "RUNNING" | "OK" | "KO" | "REPLAYED" | "CANCELLED" | "CLOSED" | "DISABLED", statusDate: string, - actionType: "PREACTIVATION_AND_ACTIVATION" | string, // todo: añadir el resto - massActionIds: number[] + actionType: ActionType + massActionIds: number[], + actionRequestReports: + { + requestId: string, + actionRequestReportDataDTOs: [ + { + data: string, + newData: string | null, + iccid: string, + dataStatus: DataStatus + } + ] + }[], } + export type DataStatus = "DATA_INVALID_FORMAT" | "DATA_NOT_FOUND" | "DATA_NOT_ACTIVATED" | "SERVICE_DATA_NOT_ACTIVATED" | + "DATA_WRONG_STATUS" | "DATA_NOT_AUTHORIZED" | "DATA_CUSTOMER_ACCOUNT_NOT_AUTHORIZED" | "DATA_AMBIGUOUS" | + "NEW_DATA_INVALID_FORMAT" | "NEW_DATA_ALREADY_EXISTS" | "DUPLICATE_DATA" | "DATA_TERMINATION_VALIDATED" | + "DATA_TERMINATION_SECURISED" | "MAX_ALARM_INSTANCE" | "MAX_ALARM_INSTANCE_TO_CATCH_UP" | + "ACTIVATED_LINE_CANNOT_BE_TRANSFERED" | "ESIM_WRONG_STEP" | "ESIM_WRONG_PAIRED_VALUE" | + "ESIM_WRONG_DOWNLOAD_STATE" | "ESIM_WRONG_STATUS" | "ESIM_WRONG_FAMILY" | "ESIM_WRONG_CATEGORY" | + "ENTITY_STATUS_NOT_AUTHORIZED" | "LONG_LIFE_NOT_ALLOWED" | "RCARD_NOT_COMPATIBLE" | "APN_NOT_FOUND" | + "APN_OR_DNN_NOT_FOUND" | "APN_CONFIGURATION_NOT_FOUND" | "APN_CONFIGURATION_INVALID_PARAMETER_FILE" | + "IP_NOT_AVAILABLE" | "RADIUS_FIELD_LENGTH_NOT_ALLOWED" | "RADIUS_LOGIN_OR_PASSWORD_NOT_FOUND" | "RADIUS_PASSWORD_NOT_ALLOWED" | + "RADIUS_LOGIN_NOT_ALLOWED" | "NETWORK_NOT_ACTIVATED" | "CHANGE_CUSTOMER_ACCOUNT_NOT_AllOWED" | "CHANGE_OFFER_NOT_ALLOWED" | + "SIM_NOT_EUICC" | "OFFER_NOT_WSF_PALIER_FLOTTE_FR" + export type ActionType = "PREACTIVATION" | "PREACTIVATION_ACTIVATION" | "ACTIVATION" | "STATUS_CHANGE" | "ICCID_CHANGE" | "EUICC_NOTIFICATION" | "EUICC_AUDIT" | "MSISDN_CHANGE" | "ALARM_SETTING" diff --git a/packages/sim-shared/infrastructure/OrderRepository.ts b/packages/sim-shared/infrastructure/OrderRepository.ts index a6c23b5..15d18f8 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.ts @@ -2,7 +2,7 @@ * TODO: Usar */ import { PoolClient, QueryResult, QueryResultRow } from "pg"; -import { CreateOrderDTO, FinishOrderDTO, OrderTracking, UpdateOrderDTO } from "../domain/Order.js"; +import { CreateOrderDTO, ErrorOrderDTO, FinishOrderDTO, OrderTracking, UpdateOrderDTO } from "../domain/Order.js"; import { Result } from "../domain/Result.js"; import { PgClient } from "./PgClient.js"; import assert from "node:assert"; @@ -353,22 +353,19 @@ export class OrderRepository { } // TODO: tema de poder filtrar por correlation_id - public async errorOrder(args: { - id: number, - status: "failed" | "dlx", - reason: string, - error?: string, - stackTrace?: string - }) { + public async errorOrder(args: ErrorOrderDTO): Promise>> { const client = await this.pgClient.connect(); await client.query('BEGIN'); + const idType = ('id' in args) ? "id" : "correlation_id" + const idValue = (args.id != undefined) ? args.id : args.correlation_id + // 1. Se consulta la order de base const qCurrentOrder = ` SELECT * FROM order_tracking - WHERE id = $1 + WHERE ${idType} = $1 ` - const vCurrentOrder = [args.id] + const vCurrentOrder = [idValue] const currentOrderResult = await this.getFirst(client.query>(qCurrentOrder, vCurrentOrder)) @@ -378,6 +375,7 @@ export class OrderRepository { return currentOrderResult } + const id = currentOrderResult.data.id // Saco el id para evitar busacr por correlation_id que es mas lento const currentOrder = currentOrderResult.data! // 3. Si todo ok se actualiza el order @@ -395,7 +393,7 @@ export class OrderRepository { WHERE id = $1 RETURNING id, status, update_date; ` - const vOrderTracking = [args.id, args.status, args.error, args.stackTrace] + const vOrderTracking = [id, args.status, args.error, args.stackTrace] const updatedOrderResult = await this.getFirst( client.query<{ id: number, status: string, update_date: string }>(uOrderTracking, vOrderTracking) ) From 9470b5605d29d05cc21205f960ba89d9488d36b1 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 27 Mar 2026 10:50:03 +0100 Subject: [PATCH 06/41] Pribando el env --- packages/sim-objenious-cron/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/sim-objenious-cron/.env b/packages/sim-objenious-cron/.env index 4df9ec5..1121f37 100644 --- a/packages/sim-objenious-cron/.env +++ b/packages/sim-objenious-cron/.env @@ -7,6 +7,6 @@ OBJ_KID=xNfbMiyL1ORXGP8lElhcv8nVaG3EJKye4Lc1YoN3I1E OBJ_BASE_URL=https://api-getway.objenious.com/ws # OBJ_BASE_URL=https://api-getway.objenious.com/ws/test -# NOTIFICATION_URL="https://sf-sim-activation.savefamilygps.net/send-activation-mail" -NOTIFICATION_URL="localhost" +NOTIFICATION_URL="https://sf-sim-activation.savefamilygps.net/send-activation-mail" +# NOTIFICATION_URL="localhost" SIM_ACTIVATION_API_KEY=9e48c4ac-1ab0-4397-b3f3-6c239200dfe6 From 61c0edca07f782efe1fb8f136361b10b2f2313d6 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 27 Mar 2026 10:52:03 +0100 Subject: [PATCH 07/41] Logs del envio --- packages/sim-objenious-cron/tasks/check_objenious_request.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index 1d158dd..ca5f571 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -300,6 +300,8 @@ export class CheckObjeniousRequests { * al servicio que manda los mails */ private async notifyFinalization(operation: ObjeniousOperation & { msisdn: string }) { + console.log("[i] Enviando activacion a", env.NOTIFICATION_URL) + console.log("[i] Operation", operation) const req = axios.post(env.NOTIFICATION_URL, { ...operation, iccids: [operation.iccids] From 9506b9e28e6dcada19229b7d852f1bc8c06ee898 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 27 Mar 2026 10:59:15 +0100 Subject: [PATCH 08/41] Error de nombre de activacion --- packages/sim-objenious-cron/tasks/check_objenious_request.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index ca5f571..05ba66b 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -137,7 +137,7 @@ export class CheckObjeniousRequests { }) } - if (originalAction.operation == "activation") { + if (originalAction.operation == "activate") { this.notifyFinalization({ ...originalAction, msisdn From 9b305f887f96588c87f3073d1a04f56362de4322 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 27 Mar 2026 12:24:20 +0100 Subject: [PATCH 09/41] Test .env ajustado --- packages/sim-objenious-cron/config/env/index.ts | 10 ++++++---- packages/sim-objenious-cron/package.json | 2 +- .../sim-shared/domain/operationsRepository.port.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/sim-objenious-cron/config/env/index.ts b/packages/sim-objenious-cron/config/env/index.ts index 229480e..bf1b5f9 100644 --- a/packages/sim-objenious-cron/config/env/index.ts +++ b/packages/sim-objenious-cron/config/env/index.ts @@ -31,15 +31,15 @@ export const env = { OBJ_KID: String(process.env.OBJ_KID), OBJ_BASE_URL: String(process.env.OBJ_BASE_URL), - NOTIFICATION_URL: String(process.env.NOTIFICATION_URL), - SIM_ACTIVATION_API_KEY: String(process.env.SIM_ACTIVATION_API_KEY) + NOTIFICATION_URL: String(process.env.NOTIFICATION_URL ?? ""), + SIM_ACTIVATION_API_KEY: String(process.env.SIM_ACTIVATION_API_KEY ?? "") }; // assert las partes criticas assert(env.RABBITMQ_PASSWORD != undefined) assert(env.RABBITMQ_USER != undefined) -assert(env.SIM_ACTIVATION_API_KEY != undefined) -assert(env.NOTIFICATION_URL != undefined) +assert(env.SIM_ACTIVATION_API_KEY != "") +assert(env.NOTIFICATION_URL != "") if (env.ENVIRONMENT == "production") { assert(env.RABBITMQ_PASSWORD != "guest") @@ -47,3 +47,5 @@ if (env.ENVIRONMENT == "production") { } +console.log("[i] verificado env") + diff --git a/packages/sim-objenious-cron/package.json b/packages/sim-objenious-cron/package.json index af8ef70..6e0d860 100644 --- a/packages/sim-objenious-cron/package.json +++ b/packages/sim-objenious-cron/package.json @@ -32,7 +32,7 @@ }, "scripts": { "test": "node --import tsx --test ./**/*.test.ts", - "build": "tsc --build && tsc-alias -p tsconfig.json && cp package.json ../../dist/packages/sim-objenious-cron/", + "build": "tsc --build && tsc-alias -p tsconfig.json && cp .env package.json ../../dist/packages/sim-objenious-cron/", "dev": "tsx watch index.ts", "start": "node ../../dist/packages/sim-objenious-cron/index.js" }, diff --git a/packages/sim-shared/domain/operationsRepository.port.ts b/packages/sim-shared/domain/operationsRepository.port.ts index 55d694f..237210e 100644 --- a/packages/sim-shared/domain/operationsRepository.port.ts +++ b/packages/sim-shared/domain/operationsRepository.port.ts @@ -12,7 +12,7 @@ export type ObjeniousOperation = { id?: number; /** Uuid del mensaje asociado a la operacion */ correlation_id?: string; - operation: string; + operation: "activate" | string; // TODO: completar y actualizar retry_count?: number; max_retry?: number; max_date_retry?: string | null; From 1b6da651a67909c07e28ce8d72bdb280311fa08c Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 27 Mar 2026 12:47:10 +0100 Subject: [PATCH 10/41] Ajustado el periodo de comprobaciones --- packages/sim-objenious-cron/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index 38a83f6..84e8cd7 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -33,7 +33,7 @@ async function startCron() { const volcadoLineasTask = new TaskVolcadoLineas(httpClient, objeniousLineRepository) - const PERIODO_PETICIONES = 10 * 60 * 60 + const PERIODO_PETICIONES = 10 * 60 * 1000 const interval = setInterval(async () => { try { await objTask.getPendingOperations() From 7d8835926367a1f2e26480e0f519c4e8d418e229 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 7 Apr 2026 13:20:31 +0200 Subject: [PATCH 11/41] Refactor de jwt y base de la bdd de pausas-cancelaciones --- .../1.2.0_Cola-pausa-cancelacion.sql | 24 ++++ .../aplication/Sim.controller.ts | 4 +- .../config/httpClient.config.ts | 4 +- .../config/jwtService.config.ts | 59 +++++++++ .../config/httpClient.config.ts | 5 +- .../config/jwtService.config.ts | 59 +++++++++ packages/sim-objenious-cron/index.ts | 15 ++- .../tasks/volcado_lineas.ts | 90 +------------ .../aplication/JWT.service.test.ts | 8 +- .../aplication/JWT.service.ts | 117 +++++++++-------- packages/sim-shared/config/config.test.ts | 13 ++ .../sim-shared/config/jwtService.config.ts | 67 ++++++++++ packages/sim-shared/domain/Result.ts | 7 +- .../ObjeniousOperationRepository.test.ts | 14 +++ .../ObjeniousOperationRepository.ts | 119 +++++++++++++++++- .../infrastructure/OrderRepository.test.ts | 2 +- .../infrastructure/OrderRepository.ts | 8 +- packages/sim-shared/test.env | 14 +++ 18 files changed, 465 insertions(+), 164 deletions(-) create mode 100644 deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql create mode 100644 packages/sim-consumidor-objenious/config/jwtService.config.ts create mode 100644 packages/sim-objenious-cron/config/jwtService.config.ts rename packages/{sim-consumidor-objenious => sim-shared}/aplication/JWT.service.test.ts (61%) rename packages/{sim-consumidor-objenious => sim-shared}/aplication/JWT.service.ts (66%) create mode 100644 packages/sim-shared/config/jwtService.config.ts create mode 100644 packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts create mode 100644 packages/sim-shared/test.env diff --git a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql new file mode 100644 index 0000000..44bb3dd --- /dev/null +++ b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql @@ -0,0 +1,24 @@ +/** +* Para la tarea WEBINT-328-Pausas-cacelaciones. +* Almacena las pausas/cancelaciones que no se han podido hacer porque la linea esta en +* "Test" +*/ + +CREATE TABLE IF NOT EXISTS pause_cancel_tasks ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + iccid TEXT NOT NULL, + + last_checked TIMESTAMPTZ, -- Última vez que se ha comprobado que no esté en test + activation_date TIMESTAMPTZ, -- Fecha de activacion para comprobar si ha pasdo un mes + next_check TIMESTAMPTZ, -- Si se ha comprobado se asignará la siguiente fecha de revision + + completed_date TIMESTAMPTZ, -- Cuando se ha completado, para bien o mal. + error TEXT +); + +-- Indice de las tareas que no han terminado +CREATE INDEX idx_pause_cancel_tasks_pending +ON pause_cancel_tasks (next_check) +WHERE completed_date IS NULL; + + diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index 7c60673..b56db69 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -3,7 +3,6 @@ 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 { env } from "#config/env/index.js"; /** * La clase usa generadores de funciones para mantener el contexto @@ -157,6 +156,9 @@ export class SimController { } } + /** + * Lo mismo que pause + */ public suspend() { return async (msg: ConsumeMessage) => { let msgData; diff --git a/packages/sim-consumidor-objenious/config/httpClient.config.ts b/packages/sim-consumidor-objenious/config/httpClient.config.ts index 6d37725..427b6e8 100644 --- a/packages/sim-consumidor-objenious/config/httpClient.config.ts +++ b/packages/sim-consumidor-objenious/config/httpClient.config.ts @@ -1,6 +1,6 @@ import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js" -import { JWTService } from "../aplication/JWT.service.js" import { env } from "./env/index.js" +import { jwtService } from "./jwtService.config.js" const OBJ_BASE_URL = env.OBJ_BASE_URL @@ -9,5 +9,5 @@ export const httpInstance = new HttpClient({ headers: { "content-type": " application/json; charset=utf-8" }, - jwtManager: new JWTService() + jwtManager: jwtService }) diff --git a/packages/sim-consumidor-objenious/config/jwtService.config.ts b/packages/sim-consumidor-objenious/config/jwtService.config.ts new file mode 100644 index 0000000..3d9bf78 --- /dev/null +++ b/packages/sim-consumidor-objenious/config/jwtService.config.ts @@ -0,0 +1,59 @@ +import { GrantAccessRequestBody, JWTService } from "sim-shared/aplication/JWT.service.js" +import { env } from "./env/index.js" +import { JWTHeader } from "sim-shared/domain/JWT.js" + + +const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH + +const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" +const REFRESH_TOKEN_URL = GET_TOKEN_URL + +const DEFAULT_BODY: GrantAccessRequestBody = { + grant_type: "client_credentials", + client_id: env.OBJ_CLIENT_ID, + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: env.OBJ_CLI_ASSERTION +} + + +const DEFAULT_HEADERS = { + "content-type": "application/x-www-form-urlencoded" +} + +const DEFAULT_HEADERS_JWT = { + alg: "RS256", + typ: "JWT", + kid: env.OBJ_KID, +} + +const DEFAULT_DATA_JWT = { + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: "https://idp.docapost.io/auth/realms/GETWAY", + jti: Date.now().toString(), + +} + +function addIATHeaders(authHeaders: Object) { + const headers = { + ...authHeaders, + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: GET_TOKEN_URL, + jti: Date.now().toString(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 5 * 60, + } + return headers +} + +export const jwtService = new JWTService({ + transformJWTHeaders: addIATHeaders, + defaultHeaders: DEFAULT_HEADERS, + defaultBody: DEFAULT_BODY, + defaultJWTHeaders: DEFAULT_HEADERS_JWT, + defaultJWTPayload: DEFAULT_DATA_JWT, + privateKeyPath: PRIVATE_KEY_PATH, + tokenUrl: GET_TOKEN_URL, + refreshTokenUrl: REFRESH_TOKEN_URL +}) diff --git a/packages/sim-objenious-cron/config/httpClient.config.ts b/packages/sim-objenious-cron/config/httpClient.config.ts index 89fe5d1..c526054 100644 --- a/packages/sim-objenious-cron/config/httpClient.config.ts +++ b/packages/sim-objenious-cron/config/httpClient.config.ts @@ -1,6 +1,7 @@ import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js" import { env } from "./env/index.js" -import { JWTService } from "packages/sim-consumidor-objenious/aplication/JWT.service.js" +import { jwtService } from "./jwtService.config.js" + const OBJ_BASE_URL = env.OBJ_BASE_URL @@ -9,5 +10,5 @@ export const httpInstance = new HttpClient({ headers: { "content-type": " application/json; charset=utf-8" }, - jwtManager: new JWTService() + jwtManager: jwtService }) diff --git a/packages/sim-objenious-cron/config/jwtService.config.ts b/packages/sim-objenious-cron/config/jwtService.config.ts new file mode 100644 index 0000000..3d9bf78 --- /dev/null +++ b/packages/sim-objenious-cron/config/jwtService.config.ts @@ -0,0 +1,59 @@ +import { GrantAccessRequestBody, JWTService } from "sim-shared/aplication/JWT.service.js" +import { env } from "./env/index.js" +import { JWTHeader } from "sim-shared/domain/JWT.js" + + +const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH + +const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" +const REFRESH_TOKEN_URL = GET_TOKEN_URL + +const DEFAULT_BODY: GrantAccessRequestBody = { + grant_type: "client_credentials", + client_id: env.OBJ_CLIENT_ID, + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: env.OBJ_CLI_ASSERTION +} + + +const DEFAULT_HEADERS = { + "content-type": "application/x-www-form-urlencoded" +} + +const DEFAULT_HEADERS_JWT = { + alg: "RS256", + typ: "JWT", + kid: env.OBJ_KID, +} + +const DEFAULT_DATA_JWT = { + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: "https://idp.docapost.io/auth/realms/GETWAY", + jti: Date.now().toString(), + +} + +function addIATHeaders(authHeaders: Object) { + const headers = { + ...authHeaders, + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: GET_TOKEN_URL, + jti: Date.now().toString(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 5 * 60, + } + return headers +} + +export const jwtService = new JWTService({ + transformJWTHeaders: addIATHeaders, + defaultHeaders: DEFAULT_HEADERS, + defaultBody: DEFAULT_BODY, + defaultJWTHeaders: DEFAULT_HEADERS_JWT, + defaultJWTPayload: DEFAULT_DATA_JWT, + privateKeyPath: PRIVATE_KEY_PATH, + tokenUrl: GET_TOKEN_URL, + refreshTokenUrl: REFRESH_TOKEN_URL +}) diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index 84e8cd7..9153d39 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -21,7 +21,10 @@ async function startCron() { console.log("[i] Comprobando conexion con la BDD ") await pgClient.checkDatabaseConnection() - const operationRepository = new ObjeniousOperationsRepository(pgClient) + const operationRepository = new ObjeniousOperationsRepository( + httpClient, + pgClient, + ) const orderRepository = new OrderRepository(pgClient) const objeniousLineRepository = new ObjeniousLinesRepository(postgresClientIntranet) @@ -31,7 +34,15 @@ async function startCron() { httpClient, ) - const volcadoLineasTask = new TaskVolcadoLineas(httpClient, objeniousLineRepository) + const objeniosRepo = new ObjeniousOperationsRepository( + httpClient, + pgClient + ) + + const volcadoLineasTask = new TaskVolcadoLineas( + objeniousLineRepository, + objeniosRepo + ) const PERIODO_PETICIONES = 10 * 60 * 1000 const interval = setInterval(async () => { diff --git a/packages/sim-objenious-cron/tasks/volcado_lineas.ts b/packages/sim-objenious-cron/tasks/volcado_lineas.ts index e8cbd8a..bea139a 100644 --- a/packages/sim-objenious-cron/tasks/volcado_lineas.ts +++ b/packages/sim-objenious-cron/tasks/volcado_lineas.ts @@ -1,94 +1,14 @@ -import assert from "node:assert"; -import { lineToCreateLineDto, ObjeniousLine, ObjeniousLineResponse } from "sim-shared/domain/objeniousLine.js"; -import { tryCatch, Result } from "sim-shared/domain/Result.js"; -import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"; +import { lineToCreateLineDto, ObjeniousLine } from "sim-shared/domain/objeniousLine.js"; import { ObjeniousLinesRepository } from "../infranstructure/ObjeniousLinesRepository.js"; -import { AxiosResponse } from "axios"; -import { constants } from "node:buffer"; - -const MAX_PAGE_SIZE = 100 +import { ObjeniousOperationsRepository } from "packages/sim-shared/infrastructure/ObjeniousOperationRepository.js"; export class TaskVolcadoLineas { constructor( - private readonly httpClient: HttpClient, private readonly linesRepository: ObjeniousLinesRepository, + private readonly objeniousRepository: ObjeniousOperationsRepository ) { } - /** - * Mover al repo - */ - private async * getLinesByStatus(args?: { - pageSize?: number, - pageNumber?: number, - status?: string - }): AsyncGenerator, Result, any> { - - const path = "/lines" - const pageSize = args?.pageSize ?? MAX_PAGE_SIZE; - - let currentPage = args?.pageNumber ?? 0; - let totalPages: number | undefined = undefined; // Como limite de paginas, igual es pasarse pero hasta que se lea - - const params: Record = {} - - const loadNextLine = async (page: number): Promise> => { - if (args?.status != undefined) params["simStatus"] = args.status - params["pageSize"] = pageSize - params["pageNumber"] = page - console.log("Params", params) - console.log(`[i] Cargando pagina ${currentPage} de ${totalPages ?? "(desc)"}`) - const nextPage = await tryCatch>(this.httpClient.client.get(path, { - params: params - })) - - if (nextPage.error != undefined) { - console.error(nextPage.error.msg) - return { - error: nextPage.error.msg.message - } - } - - // Se aumenta para la siguiente ejecucion - console.log(`[i] Página ${currentPage} completa, total: ${nextPage.data.data.totalPages}`) - totalPages = nextPage.data.data.totalPages - - return { - data: nextPage.data.data.content - } - - } - - // El inicio se ejecuta siempre - const lines = await loadNextLine(currentPage) - - if (lines.error != undefined) { - console.error("[x] Error obteniendo las lineas, cancelando operación"); - return { - error: "Error cargando lineas" - } - } - - currentPage++; - - yield { - data: lines.data - } - - // Copia para evitar bucles infinitos por error de la api - const maxPages = totalPages - assert.ok(maxPages != undefined, "No se ha defindo el numero de paginas") // Nunca deberia pasar pero así se evitan bucles infnitos - console.log("maxPages", maxPages) - for (let i = currentPage; i < maxPages!; i++) { - console.log("Bucle i:", i, "page: ", currentPage) - yield await loadNextLine(currentPage); - currentPage++; - } - - return { - data: [] - } - } private async saveLines(lines: ObjeniousLine[]) { const linesToCreate = lines.map(lineToCreateLineDto) @@ -107,7 +27,9 @@ export class TaskVolcadoLineas { console.log("[i] Iniciando task de volcado de lineas de Objenious") // Carga todas las lineas en memoria, hay que comprobar que no se gaste demasiada - const linesIterator = this.getLinesByStatus() + const linesIterator = this.objeniousRepository.getLinesByStatusAPI({ + pageSize: 100 + }) let lines = await linesIterator.next() if (lines.value.error != undefined || lines.value.data == undefined) { diff --git a/packages/sim-consumidor-objenious/aplication/JWT.service.test.ts b/packages/sim-shared/aplication/JWT.service.test.ts similarity index 61% rename from packages/sim-consumidor-objenious/aplication/JWT.service.test.ts rename to packages/sim-shared/aplication/JWT.service.test.ts index d7f2f43..1d37d7e 100644 --- a/packages/sim-consumidor-objenious/aplication/JWT.service.test.ts +++ b/packages/sim-shared/aplication/JWT.service.test.ts @@ -1,16 +1,16 @@ import { test, describe } from "vitest" -import { JWTService } from "./JWT.service.js" +import { jwtService } from "../config/jwtService.config.js" describe("Tokens Objenious", () => { - const jwtService = new JWTService() + const jwt = jwtService test("Solicicitud normal de auth", async () => { - const token = await jwtService.getAccessToken() + const token = await jwt.getAccessToken() console.log("acceso objenious", token) }), test("Solicicitud de refresh de auth", async () => { - const token = await jwtService.tryRefreshToken() + const token = await jwt.tryRefreshToken() console.log("acceso refresh objenious", token) }) }) diff --git a/packages/sim-consumidor-objenious/aplication/JWT.service.ts b/packages/sim-shared/aplication/JWT.service.ts similarity index 66% rename from packages/sim-consumidor-objenious/aplication/JWT.service.ts rename to packages/sim-shared/aplication/JWT.service.ts index 038cfb8..9ef739e 100644 --- a/packages/sim-consumidor-objenious/aplication/JWT.service.ts +++ b/packages/sim-shared/aplication/JWT.service.ts @@ -4,24 +4,24 @@ * el cliente HTTP */ -import { env } from "#config/env/index.js"; import fs from "fs" import { JWTToken, JWTHeader, - IJWTService + IJWTService, + JWTPayload } from "sim-shared/domain/JWT.js" import axios, { AxiosError } from "axios"; -type GrantAccessRequestBody = { +export type GrantAccessRequestBody = { grant_type: string, client_id: string, client_assertion_type: string, client_assertion: string } -type TokensRequestResponse = { +export type TokensRequestResponse = { "access_token": string, "expires_in": number, "refresh_token": string @@ -32,41 +32,6 @@ type TokensRequestResponse = { "scope": string } - -const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH - -const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" -const REFRESH_TOKEN_URL = GET_TOKEN_URL - -const DEFAULT_BODY: GrantAccessRequestBody = { - grant_type: "client_credentials", - client_id: env.OBJ_CLIENT_ID, - client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", - client_assertion: env.OBJ_CLI_ASSERTION -} - -const REFRESH_BODY = { - ...DEFAULT_BODY, - grant_type: "refresh_token", -} - -const DEFAULT_HEADERS = { - "content-type": "application/x-www-form-urlencoded" -} - -function addIATHeaders(authHeaders: Object) { - const headers = { - ...authHeaders, - sub: env.OBJ_CLIENT_ID, - iss: env.OBJ_CLIENT_ID, - aud: GET_TOKEN_URL, - jti: Date.now().toString(), - iat: Math.floor(Date.now() / 1000), - exp: Math.floor(Date.now() / 1000) + 5 * 60, - } - return headers -} - export type ObjeniousTokenBody = any /** @@ -82,27 +47,54 @@ export class JWTService implements IJWTService { public authToken: JWTToken | undefined; private refreshToken?: JWTToken | undefined; - constructor(args?: { + // http + private transformHeaders?: (_: Object) => JWTHeader; + private defaultHttpHeaders: Record; + private defaultBody: Record; + + // jwt + private defaultJWTHeaders: JWTHeader; + private defaultJWTPayload: JWTPayload; + private privateKeyPath: string; + private tokenUrl: string; + private refreshTokenUrl: string; + + + constructor(args: { token?: string // si se partiese de un token existente, - refreshToken?: string + refreshToken?: string, + transformJWTHeaders?: (_: Object) => JWTHeader, + defaultHeaders: Record, + defaultBody: Record, + defaultJWTHeaders: JWTHeader, + defaultJWTPayload: JWTPayload, + privateKeyPath: string, + tokenUrl: string, + refreshTokenUrl: string }) { if (args?.token != undefined) this.authToken = new JWTToken(args.token) if (args?.refreshToken != undefined) this.refreshToken = new JWTToken(args.refreshToken) + if (args?.transformJWTHeaders != undefined) this.transformHeaders = args.transformJWTHeaders + + this.defaultHttpHeaders = args.defaultHeaders; + this.defaultBody = args.defaultBody; + + this.defaultJWTHeaders = args.defaultJWTHeaders; + this.defaultJWTPayload = args.defaultJWTPayload; + this.privateKeyPath = args.privateKeyPath; + + this.tokenUrl = args.tokenUrl; + this.refreshTokenUrl = args.refreshTokenUrl; } private buildJwtBody() { - const jwtHeaders = { - alg: "RS256", - typ: "JWT", - kid: env.OBJ_KID - } - const jwtData = addIATHeaders({ - sub: env.OBJ_CLIENT_ID, - iss: env.OBJ_CLIENT_ID, - aud: "https://idp.docapost.io/auth/realms/GETWAY", - jti: Date.now().toString(), - }) - const key = fs.readFileSync(PRIVATE_KEY_PATH, "utf8") + const jwtHeaders = this.defaultJWTHeaders + + const jwtData = (this.transformHeaders) ? + this.transformHeaders(this.defaultJWTPayload) : + this.defaultJWTPayload; + + const key = fs.readFileSync(this.privateKeyPath, "utf8") const token = JWTToken.fromParts({ header: jwtHeaders, payload: jwtData, @@ -116,14 +108,16 @@ export class JWTService implements IJWTService { public async getNewAuthToken() { const bodyWithtoken = { - ...DEFAULT_BODY, + ...this.defaultBody, client_assertion: this.buildJwtBody() } - const req = axios.post(GET_TOKEN_URL, + const headers = (this.transformHeaders) ? this.transformHeaders(this.defaultHttpHeaders) : this.defaultHttpHeaders; + + const req = axios.post(this.tokenUrl, bodyWithtoken, { - headers: addIATHeaders(DEFAULT_HEADERS) + headers: headers } ) @@ -166,16 +160,21 @@ export class JWTService implements IJWTService { if (this.refreshToken == undefined) throw new Error("El refreshToken no está definido") if (this.refreshToken.isExpired()) throw new Error("El refreshToken ha expirado") + const refreshBody = { + ...this.defaultBody, + grant_type: "refresh_token", + } + const body = { - ...REFRESH_BODY, + ...refreshBody, client_assertion: this.buildJwtBody(), refresh_token: this.refreshToken.rawToken } - const req = axios.post(REFRESH_TOKEN_URL, + const req = axios.post(this.refreshTokenUrl, body, { - headers: DEFAULT_HEADERS + headers: this.defaultHttpHeaders } ) diff --git a/packages/sim-shared/config/config.test.ts b/packages/sim-shared/config/config.test.ts index 22fb083..b8e2c7c 100644 --- a/packages/sim-shared/config/config.test.ts +++ b/packages/sim-shared/config/config.test.ts @@ -7,9 +7,12 @@ import { env, loadEnvFile } from "node:process"; import { Pool } from "pg"; import { PgClient } from "../infrastructure/PgClient.js"; +import { HttpClient } from "../infrastructure/HTTPClient.js"; +import { jwtService } from "./jwtService.config.js"; console.warn("[i!] Se está corriendo codigo de test") loadEnvFile("../../.env") // Global +loadEnvFile("./test.env") // Local // se hace una por servicio. export const pgPool = new Pool({ @@ -24,4 +27,14 @@ export const postgresClient = new PgClient({ pool: pgPool }) +const OBJ_BASE_URL = "https://api-getway.objenious.com/ws" +export const httpObjClient = new HttpClient({ + baseURL: OBJ_BASE_URL, + headers: { + "content-type": " application/json; charset=utf-8" + }, + jwtManager: jwtService +}) + + console.warn(`[T] TEST DB : ${env.POSTGRES_DATABASE}@${env.POSTGRES_HOST}`) diff --git a/packages/sim-shared/config/jwtService.config.ts b/packages/sim-shared/config/jwtService.config.ts new file mode 100644 index 0000000..c2500b8 --- /dev/null +++ b/packages/sim-shared/config/jwtService.config.ts @@ -0,0 +1,67 @@ +import assert from "assert" +import { env, loadEnvFile } from "process" +import { GrantAccessRequestBody, JWTService } from "sim-shared/aplication/JWT.service.js" +import { JWTHeader } from "sim-shared/domain/JWT.js" + +loadEnvFile("../../.env") // Global +loadEnvFile("./test.env") // Local + +assert(env.OBJ_CLIENT_ID != undefined) +assert(env.OBJ_CLI_ASSERTION != undefined) +assert(env.OBJ_PEM_PATH != undefined) + +const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH + +const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" +const REFRESH_TOKEN_URL = GET_TOKEN_URL + + +const DEFAULT_BODY: GrantAccessRequestBody = { + grant_type: "client_credentials", + client_id: env.OBJ_CLIENT_ID, + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: env.OBJ_CLI_ASSERTION +} + + +const DEFAULT_HEADERS = { + "content-type": "application/x-www-form-urlencoded" +} + +const DEFAULT_HEADERS_JWT = { + alg: "RS256", + typ: "JWT", + kid: env.OBJ_KID, +} + +const DEFAULT_DATA_JWT = { + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: "https://idp.docapost.io/auth/realms/GETWAY", + jti: Date.now().toString(), + +} + +function addIATHeaders(authHeaders: Object) { + const headers = { + ...authHeaders, + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: GET_TOKEN_URL, + jti: Date.now().toString(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 5 * 60, + } + return headers +} + +export const jwtService = new JWTService({ + transformJWTHeaders: addIATHeaders, + defaultHeaders: DEFAULT_HEADERS, + defaultBody: DEFAULT_BODY, + defaultJWTHeaders: DEFAULT_HEADERS_JWT, + defaultJWTPayload: DEFAULT_DATA_JWT, + privateKeyPath: PRIVATE_KEY_PATH, + tokenUrl: GET_TOKEN_URL, + refreshTokenUrl: REFRESH_TOKEN_URL +}) diff --git a/packages/sim-shared/domain/Result.ts b/packages/sim-shared/domain/Result.ts index a8bda25..0381424 100644 --- a/packages/sim-shared/domain/Result.ts +++ b/packages/sim-shared/domain/Result.ts @@ -14,7 +14,7 @@ export type Failure = { */ export type Result = Failure | Success -export async function tryCatch(func: Promise): Promise> { +export async function tryCatch(func: Promise): Promise> { try { const res = await func; return { @@ -22,9 +22,8 @@ export async function tryCatch(func: Promise): Promise { + const repository = new ObjeniousOperationsRepository( + httpObjClient, + postgresClient + ) + + it("Read /lines with multiple iccids", () => { + + }) +}) diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index 134da3f..a48523a 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -1,14 +1,131 @@ import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "sim-shared/domain/operationsRepository.port.js"; -import { Result } from "sim-shared/domain/Result.js"; +import { Result, tryCatch } from "sim-shared/domain/Result.js"; import { PgClient } from "sim-shared/infrastructure/PgClient.js"; +import { ObjeniousLine, ObjeniousLineResponse } from "../domain/objeniousLine.js"; +import { HttpClient } from "./HTTPClient.js"; +import assert from "node:assert"; +import { AxiosResponse } from "axios"; export class ObjeniousOperationsRepository implements IOperationsRepository { constructor( + private http: HttpClient, private readonly pgClient: PgClient ) { } + /** + * Consulta el estado de una o mas lineas directamente a la API de Objenious + */ + public async getLinesAPI( + identifierType: "ICCID" | "IMSI" | "IMEI" | "MSISDN" | "REFERENCE", + identifiers: string[] + ): Promise> { + if (identifiers.length == 0) { + return { + data: [] + } + } + + // Comprobar < MAX_PAGE_SIZE (Poco probable) + + const path = "/lines" + const params = { + "identifier.identifierType": identifierType, + "identifier.identifiers": identifiers.toString() + } + + const req = this.http.client.get(path, { + params: params + }) + + const res = await tryCatch(req) + + if (res.error != undefined) { + return { + error: res.error?.message + } + } + + const lines = res.data.data + + return { + data: lines + } + } + + + private MAX_PAGE_SIZE = 1000 + public async * getLinesByStatusAPI(args?: { + pageSize?: number, + pageNumber?: number, + status?: string + }): AsyncGenerator, Result, any> { + + const path = "/lines" + const pageSize = args?.pageSize ?? this.MAX_PAGE_SIZE; + + let currentPage = args?.pageNumber ?? 0; + let totalPages: number | undefined = undefined; // Como limite de paginas, igual es pasarse pero hasta que se lea + + const params: Record = {} + + const loadNextLine = async (page: number): Promise> => { + if (args?.status != undefined) params["simStatus"] = args.status + params["pageSize"] = pageSize + params["pageNumber"] = page + console.log(`[i] Cargando pagina ${currentPage} de ${totalPages ?? "(desc)"}`) + const nextPage = await tryCatch>(this.http.client.get(path, { + params: params + })) + + if (nextPage.error != undefined) { + console.error(nextPage.error) + return { + error: nextPage.error.message + } + } + + // Se aumenta para la siguiente ejecucion + console.log(`[i] Página ${currentPage} completa, total: ${nextPage.data.data.totalPages}`) + totalPages = nextPage.data.data.totalPages + + return { + data: nextPage.data.data.content + } + + } + + // El inicio se ejecuta siempre + const lines = await loadNextLine(currentPage) + + if (lines.error != undefined) { + console.error("[x] Error obteniendo las lineas, cancelando operación"); + return { + error: "Error cargando lineas" + } + } + + currentPage++; + + yield { + data: lines.data + } + + // Copia para evitar bucles infinitos por error de la api + const maxPages = totalPages + assert.ok(maxPages != undefined, "No se ha defindo el numero de paginas") // Nunca deberia pasar pero así se evitan bucles infnitos + console.log("maxPages", maxPages) + for (let i = currentPage; i < maxPages!; i++) { + console.log("Bucle i:", i, "page: ", currentPage) + yield await loadNextLine(currentPage); + currentPage++; + } + + return { + data: [] + } + } async createOperation(data: ObjeniousOperation): Promise> { const query = ` INSERT INTO objenious_operation (operation, iccids, status, max_retry, request_id) diff --git a/packages/sim-shared/infrastructure/OrderRepository.test.ts b/packages/sim-shared/infrastructure/OrderRepository.test.ts index 8b33947..4636fe3 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.test.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.test.ts @@ -27,7 +27,7 @@ describe("Test OrderRepository", {}, (ctx) => { before(async () => { // Order1 const result1 = await orderRepo.createOrder(order1) - assert(result1.data != undefined) + assert.ok(result1.data != undefined, result1.error as string) testIds.push(result1.data.id) // Order2 -> Para el test de crearOrder diff --git a/packages/sim-shared/infrastructure/OrderRepository.ts b/packages/sim-shared/infrastructure/OrderRepository.ts index 15d18f8..b2e965a 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.ts @@ -3,10 +3,9 @@ */ import { PoolClient, QueryResult, QueryResultRow } from "pg"; import { CreateOrderDTO, ErrorOrderDTO, FinishOrderDTO, OrderTracking, UpdateOrderDTO } from "../domain/Order.js"; -import { Result } from "../domain/Result.js"; +import { Result, tryCatch } from "../domain/Result.js"; import { PgClient } from "./PgClient.js"; import assert from "node:assert"; -import { error } from "node:console"; /** * Agrupa todas las operaciones de *Order*. @@ -19,9 +18,8 @@ import { error } from "node:console"; */ export class OrderRepository { constructor( - private readonly pgClient: PgClient + private readonly pgClient: PgClient, ) { - } /** @@ -57,6 +55,8 @@ export class OrderRepository { } } + + /** * El tipo representa el contenido del mensaje de los order */ diff --git a/packages/sim-shared/test.env b/packages/sim-shared/test.env new file mode 100644 index 0000000..1e5983d --- /dev/null +++ b/packages/sim-shared/test.env @@ -0,0 +1,14 @@ +## ENV PARA DATOS DE TEST - shared nunca se lanza en produccion + +# claves de Objenious +OBJ_PEM_PATH=./obj.pem +OBJ_AUTHORIZATION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ +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 +# OBJ_BASE_URL=https://api-getway.objenious.com/ws/test + +NOTIFICATION_URL="https://sf-sim-activation.savefamilygps.net/send-activation-mail" +# NOTIFICATION_URL="localhost" +SIM_ACTIVATION_API_KEY=9e48c4ac-1ab0-4397-b3f3-6c239200dfe6 From 39567970202f5492ff976ae44c31bde1db54a3be Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 7 Apr 2026 15:40:19 +0200 Subject: [PATCH 12/41] Las operaciones basicas del repositorio de pause/cancel funcionan y tienen test --- .../PauseCancelTaskRepository.test.ts | 57 +++++++++ .../PauseCancelTaskRepository.ts | 121 ++++++++++++++++++ .../sim-consumidor-objenious/package.json | 2 +- 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts create mode 100644 packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts new file mode 100644 index 0000000..0d0e403 --- /dev/null +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts @@ -0,0 +1,57 @@ +import { after, before, describe, it } from "node:test"; +import { CreatePauseCancelTaskDTO, PauseCancelTaskRepository } from "./PauseCancelTaskRepository.js"; +import { postgrClient } from "#config/postgreConfig.js"; +import assert from "node:assert"; + +const testTask: CreatePauseCancelTaskDTO = { + iccid: "1234", + activation_date: new Date(), + next_check: new Date() +} + +describe("Test PauseCancelTaskRepository - DB", () => { + function clean() { + + } + + const createdIds: number[] = []; + + const pauseRepo = new PauseCancelTaskRepository(postgrClient) + + before(() => { + + }) + after(() => { + + }) + + it("Should create a task", async () => { + const created = await pauseRepo.addTask(testTask) + assert.ok(created != undefined, "A value must be returned always") + assert.ok(created.error == undefined, "Should not return a error") + assert.ok(created.data != undefined, "Data mus be returned") + createdIds.push(created.data.id) + }) + + it("Should update a existing task", async () => { + const updated = await pauseRepo.updateTask({ + id: createdIds[0], + next_check: new Date() + }) + + assert.ok(updated != undefined, "A value must be returned always") + assert.ok(updated.error == undefined, "Should not return a error") + assert.ok(updated.data != undefined, "Data mus be returned") + }) + + it("Should finish a existing task", async () => { + const finish = await pauseRepo.finishTask({ + id: createdIds[0], + error: "ok" + }) + + assert.ok(finish != undefined, "A value must be returned always") + assert.ok(finish.error == undefined, "Should not return a error") + assert.ok(finish.data != undefined, "Data mus be returned") + }) +}) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts new file mode 100644 index 0000000..ced1702 --- /dev/null +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts @@ -0,0 +1,121 @@ +import { Result } from "sim-shared/domain/Result.js"; +import { QueryResult } from "pg"; +import { PgClient } from "sim-shared/infrastructure/PgClient.js"; +import { AxiosError } from "axios"; + +export type PauseCancelTask = { + id: number; + iccid: string; + last_checked?: Date | null; + activation_date?: Date | null; + next_check?: Date | null; + completed_date?: Date | null; + error?: string | null; +} + +export type CreatePauseCancelTaskDTO = Pick +export type UpdatePauseCancelTaskDTO = Pick +export type FinishPauseCancelTaskDTO = Pick + +export class PauseCancelTaskRepository { + constructor( + private readonly pgClient: PgClient + ) { + + } + + /** + * Obtiene las siguientes que se pueden lanzar, puede haber más pero + * estan pendientes + */ + public async getPending(): Promise> { + const sql = ` + SELECT * FROM pause_cancel_tasks + WHERE completed_date IS NULL + AND (next_check <= NOW() OR next_check IS NULL) + ORDER BY id ASC; + `; + + try { + const res: QueryResult = await this.pgClient.query(sql); + return { + data: res.rows + } + } catch (e) { + return { + error: (e as AxiosError).message + } + } + } + + public async addTask(task: CreatePauseCancelTaskDTO): Promise> { + + const sql = ` + INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked) + VALUES ($1, $2, $3, now()) + RETURNING *; + `; + try { + const values = [task.iccid, task.activation_date, task.next_check]; + const res: QueryResult = await this.pgClient.query(sql, values); + return { + data: res.rows[0] + } + } catch (e) { + return { + error: (e as AxiosError).message + } + } + + } + + /** + * Se ha vuelto a comprobar la tarea pero sigue en test + */ + public async updateTask(updateData: UpdatePauseCancelTaskDTO): Promise> { + + const sql = ` + UPDATE pause_cancel_tasks + SET last_checked = now(), next_check = $1 + WHERE id = $2 + RETURNING *; + `; + try { + const res = await this.pgClient.query(sql, [updateData.next_check, updateData.id]); + return { + data: res.rows[0] + } + } catch (e) { + return { + error: (e as AxiosError).message + } + + } + } + + + /** + * La tarea ha termiando bien o mal + */ + public async finishTask(finishData: FinishPauseCancelTaskDTO) { + const sql = ` + UPDATE pause_cancel_tasks + SET completed_date = NOW(), error = $1 + WHERE id = $2 + RETURNING *; + `; + + try { + const res = await this.pgClient.query(sql, [finishData.error, finishData.id]); + return { + data: res.rows[0] + } + } catch (e) { + return { + error: (e as AxiosError).message + } + + } + } + +} diff --git a/packages/sim-consumidor-objenious/package.json b/packages/sim-consumidor-objenious/package.json index 135c24e..5fc7f6e 100644 --- a/packages/sim-consumidor-objenious/package.json +++ b/packages/sim-consumidor-objenious/package.json @@ -53,7 +53,7 @@ } }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "node --import tsx --test ./**/*.test.ts", "dev": "tsx watch index.ts", "build": "tsc --build && yarn tsc-alias -p tsconfig.json && cp .env package.json ../../dist/packages/sim-consumidor-objenious/", "start": "node ../../dist/packages/sim-consumidor-objenious/index.js", From e6ff54a15dbfd0e1f11a5877462bc877076e8d64 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 7 Apr 2026 17:43:17 +0200 Subject: [PATCH 13/41] Usecases --- .../1.2.0_Cola-pausa-cancelacion.sql | 4 +- .../aplication/Sim.controller.ts | 2 +- .../aplication/Sim.usecases.ts | 101 +++++++++++++++++- packages/sim-consumidor-objenious/index.ts | 11 +- .../PauseCancelTaskRepository.test.ts | 2 + .../PauseCancelTaskRepository.ts | 15 +-- 6 files changed, 121 insertions(+), 14 deletions(-) diff --git a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql index 44bb3dd..63bdba7 100644 --- a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql +++ b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql @@ -4,10 +4,12 @@ * "Test" */ +CREATE TYPE SUSPENDTERMINATE AS ENUM ('suspend','terminate'); + CREATE TABLE IF NOT EXISTS pause_cancel_tasks ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, iccid TEXT NOT NULL, - + operation_type SUSPENDTERMINATE, last_checked TIMESTAMPTZ, -- Última vez que se ha comprobado que no esté en test activation_date TIMESTAMPTZ, -- Fecha de activacion para comprobar si ha pasdo un mes next_check TIMESTAMPTZ, -- Si se ha comprobado se asignará la siguiente fecha de revision diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index b56db69..d50f59a 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -178,7 +178,7 @@ export class SimController { dueDate: this.genDueDate(2 * 60).toISOString(), identifier: { identifierType: "ICCID", - identifiers: [iccid] + identifiers: [iccid] // Por algún motivo solo he puesto un iccd por identifier } })) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index f7c79a5..bf14e22 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -5,6 +5,7 @@ import { Result } from "sim-shared/domain/Result.js" import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } from "sim-shared/domain/operationsRepository.port.js" import assert from "node:assert" import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js" +import { CreatePauseCancelTaskDTO, PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js" // TODO: // - Pasar a un archivo de DTOs @@ -12,21 +13,24 @@ import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js" export class SimUseCases { private readonly httpClient: HttpClient - private readonly operationRepository: OperationsRepositoryPort + private readonly objeniousRepository: OperationsRepositoryPort private readonly orderRepository: OrderRepository + private readonly pauseRepository: PauseCancelTaskRepository constructor(args: { httpClient: HttpClient, operationRepository: OperationsRepositoryPort, - orderRepository: OrderRepository + orderRepository: OrderRepository, + pauseRepository: PauseCancelTaskRepository }) { this.httpClient = args.httpClient - this.operationRepository = args.operationRepository + this.objeniousRepository = args.operationRepository this.orderRepository = args.orderRepository + this.pauseRepository = args.pauseRepository } private async logOperation(data: ObjeniousOperation) { - await this.operationRepository.createOperation({ + await this.objeniousRepository.createOperation({ ...data }) } @@ -73,6 +77,8 @@ export class SimUseCases { request_id: response.data.requestId } + // TODO: Esto tiene poco sentido si la operacion ya se + // tenia que haber creado en el generador this.logOperation(operation) .then().catch(e => console.error(e)) @@ -238,6 +244,93 @@ export class SimUseCases { }) } + /** + * Paso previo a la suspension para evitar errores cuando el billing es test + */ + public stage_suspend(suspendData: ActionData): () => Promise> { + return async (): Promise> => { + const correlation_id = suspendData.correlation_id + + const newTask: CreatePauseCancelTaskDTO = { + iccid: suspendData.identifier.identifiers[0], + activation_date: new Date(), // TODO: BUSCAR LA DE VERDAD + next_check: undefined, // Que se haga instantaneamente al ser la primera + operation_type: "terminate" + } + + const taskCreated = await this.pauseRepository.addTask(newTask) + + // Caso que la task no se pueda crear en la BDD + if (taskCreated.error != undefined) { + console.error("[Sim.usecases]", taskCreated.error) + if (correlation_id != undefined) { + this.orderRepository.updateOrder({ + correlation_id: correlation_id, + new_status: "failed" + }) + } + return { + error: taskCreated.error + } + } + + // Caso que se haya creado en la BDD + if (correlation_id != undefined) { + this.orderRepository.updateOrder({ + correlation_id: correlation_id, + new_status: "running" + }) + } + + return { + data: true + } + } + } + + /** + * Paso previo a la suspension para evitar errores cuando el billing es test + */ + public stage_terminate(terminateData: ActionData): () => Promise> { + return async (): Promise> => { + const correlation_id = terminateData.correlation_id + + const newTask: CreatePauseCancelTaskDTO = { + iccid: terminateData.identifier.identifiers[0], + activation_date: new Date(), // TODO: BUSCAR LA DE VERDAD + next_check: undefined, // Que se haga instantaneamente al ser la primera + operation_type: "terminate" + } + + const taskCreated = await this.pauseRepository.addTask(newTask) + + // Caso que la task no se pueda crear en la BDD + if (taskCreated.error != undefined) { + console.error("[Sim.usecases]", taskCreated.error) + if (correlation_id != undefined) { + this.orderRepository.updateOrder({ + correlation_id: correlation_id, + new_status: "failed" + }) + } + return { + error: taskCreated.error + } + } + + // Caso que se haya creado en la BDD + if (correlation_id != undefined) { + this.orderRepository.updateOrder({ + correlation_id: correlation_id, + new_status: "running" + }) + } + + return { + data: true + } + } + } public terminate(terminationData: ActionData): () => Promise> { const OPERATION_URL = "/actions/terminateLine" return this.generateUseCase({ diff --git a/packages/sim-consumidor-objenious/index.ts b/packages/sim-consumidor-objenious/index.ts index c2d836c..01e9d17 100644 --- a/packages/sim-consumidor-objenious/index.ts +++ b/packages/sim-consumidor-objenious/index.ts @@ -8,6 +8,7 @@ import { SimUseCases } from "./aplication/Sim.usecases.js" import { SimController } from "./aplication/Sim.controller.js" import { SimRouter } from "./aplication/Sim.router.js" import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js" +import { PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js" async function startWorker() { const rmqClient = await startRMQClient() @@ -18,15 +19,21 @@ async function startWorker() { await pgClient.checkDatabaseConnection() - const operationRepository = new ObjeniousOperationsRepository(pgClient) + const operationRepository = new ObjeniousOperationsRepository( + httpClient, + pgClient, + ) const orderRepository = new OrderRepository(pgClient) + const pauseRepository = new PauseCancelTaskRepository(pgClient) + const simActivationController = new SimController( rmqClient, new SimUseCases({ httpClient: httpClient, operationRepository: operationRepository, - orderRepository: orderRepository + orderRepository: orderRepository, + pauseRepository: pauseRepository }) ) const simRouter = new SimRouter(simActivationController, rmqClient) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts index 0d0e403..4facb2c 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts @@ -5,6 +5,7 @@ import assert from "node:assert"; const testTask: CreatePauseCancelTaskDTO = { iccid: "1234", + operation_type: "suspend", activation_date: new Date(), next_check: new Date() } @@ -21,6 +22,7 @@ describe("Test PauseCancelTaskRepository - DB", () => { before(() => { }) + after(() => { }) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts index ced1702..b861c7d 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts @@ -6,6 +6,7 @@ import { AxiosError } from "axios"; export type PauseCancelTask = { id: number; iccid: string; + operation_type: "suspend" | "terminate", last_checked?: Date | null; activation_date?: Date | null; next_check?: Date | null; @@ -13,15 +14,18 @@ export type PauseCancelTask = { error?: string | null; } -export type CreatePauseCancelTaskDTO = Pick +export type CreatePauseCancelTaskDTO = Pick export type UpdatePauseCancelTaskDTO = Pick export type FinishPauseCancelTaskDTO = Pick +/** + * Repositorio para compensar los problemas de cacelcaiones/pausas de objenious a + * la hora aplicarlo sobre una linea con el billing a test. + */ export class PauseCancelTaskRepository { constructor( private readonly pgClient: PgClient ) { - } /** @@ -51,12 +55,12 @@ export class PauseCancelTaskRepository { public async addTask(task: CreatePauseCancelTaskDTO): Promise> { const sql = ` - INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked) - VALUES ($1, $2, $3, now()) + INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked, operation_type) + VALUES ($1, $2, $3, now(), $4) RETURNING *; `; try { - const values = [task.iccid, task.activation_date, task.next_check]; + const values = [task.iccid, task.activation_date, task.next_check, task.operation_type]; const res: QueryResult = await this.pgClient.query(sql, values); return { data: res.rows[0] @@ -66,7 +70,6 @@ export class PauseCancelTaskRepository { error: (e as AxiosError).message } } - } /** From 4168949b9e7e2e88b587286eb35020dfe531c025 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 8 Apr 2026 10:08:54 +0200 Subject: [PATCH 14/41] Endpoint preparados --- .../aplication/Sim.controller.test.ts | 112 ++++++++++++++++++ .../aplication/Sim.controller.ts | 21 +++- .../aplication/Sim.usecases.ts | 3 +- 3 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts new file mode 100644 index 0000000..f2f3da9 --- /dev/null +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts @@ -0,0 +1,112 @@ +import { describe, it, beforeEach, mock, after } from "node:test"; +import assert from "node:assert"; +import { SimController } from "./Sim.controller.js"; +import { EventBus } from "sim-shared/domain/EventBus.port.js"; +import { SimUseCases } from "./Sim.usecases.js"; +import { ConsumeMessage } from "amqplib"; +import { postgrClient } from "#config/postgreConfig.js"; +import { httpInstance } from "#config/httpClient.config.js"; +import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"; +import { PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js"; +import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js"; + +describe("SimController Integration Tests (Real UseCases)", () => { + let eventBusMock: any; + let controller: SimController; + let useCases: SimUseCases; + + beforeEach(() => { + // Mock ONLY the event bus as requested + eventBusMock = { + publish: mock.fn(), + addSubscribers: mock.fn(), + consume: mock.fn(), + ack: mock.fn(async () => { }), + nack: mock.fn(async () => { }), + }; + + const operationRepository = new ObjeniousOperationsRepository( + httpInstance, + postgrClient, + ); + const orderRepository = new OrderRepository(postgrClient); + const pauseRepository = new PauseCancelTaskRepository(postgrClient); + + useCases = new SimUseCases({ + httpClient: httpInstance, + operationRepository: operationRepository, + orderRepository: orderRepository, + pauseRepository: pauseRepository + }); + + controller = new SimController(eventBusMock as unknown as EventBus, useCases); + }); + + const createMockMsg = (payload: any): ConsumeMessage => { + return { + content: Buffer.from(JSON.stringify(payload)), + fields: {}, + properties: { + headers: { + message_id: "test-correlation-id" + } + }, + } as unknown as ConsumeMessage; + }; + + describe("suspend", () => { + it("should call stage_suspend and interact with DB and EventBus", async () => { + const iccid = "test-iccid-suspend-" + Date.now(); + const msg = createMockMsg({ + key: "sim.test.pause", + payload: { + iccid: iccid + }, + headers: { + message_id: "correlation-suspend-" + iccid + } + }); + + const handler = controller.suspend(); + await handler(msg); + + // Verify that it reached the stage_suspend logic (which adds to pauseRepository) + // We can query the DB or check if ACK was called + assert.strictEqual(eventBusMock.ack.mock.callCount(), 1, "Message should be ACKed on success"); + assert.strictEqual(eventBusMock.nack.mock.callCount(), 0, "Message should not be NACKed"); + }); + }); + + describe("terminate", () => { + it("should call stage_terminate and interact with DB and EventBus", async () => { + const iccid = "test-iccid-terminate-" + Date.now(); + const msg = createMockMsg({ + key: "sim.test.pause", + payload: { + iccid: iccid + }, + headers: { + message_id: "correlation-terminate-" + iccid + } + }); + + const handler = controller.terminate(); + await handler(msg); + + assert.strictEqual(eventBusMock.ack.mock.callCount(), 1, "Message should be ACKed on success"); + assert.strictEqual(eventBusMock.nack.mock.callCount(), 0, "Message should not be NACKed"); + }); + }); + + describe("Error Handling", () => { + it("should nack if message is invalid", async () => { + const msg = { + content: Buffer.from("invalid json"), + fields: {}, + properties: {}, + } as unknown as ConsumeMessage; + const handler = controller.suspend(); + await assert.rejects(handler(msg), "Error de suspension consumiendo el mensaje no es valido"); + }); + }); +}); diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index d50f59a..ca103a0 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -3,6 +3,7 @@ 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"; /** * La clase usa generadores de funciones para mantener el contexto @@ -36,6 +37,7 @@ export class SimController { } 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; } @@ -173,14 +175,19 @@ export class SimController { } const iccid = msgData.payload.iccid - const res = await this.tryUseCase(msg, this.useCases.suspend({ + 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)) + */ } } @@ -197,16 +204,20 @@ export class SimController { if (msgData == undefined) { return Promise.reject("Mensaje invalido") } + const iccid = msgData.payload.iccid - console.log("Mensaje procesado", msgData) - const res = await this.tryUseCase(msg, this.useCases.terminate({ + 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)) + } } diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index bf14e22..0048a7a 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -95,7 +95,6 @@ export class SimUseCases { error: undefined, data: true } - } else { return { error: String(response.status), @@ -255,7 +254,7 @@ export class SimUseCases { iccid: suspendData.identifier.identifiers[0], activation_date: new Date(), // TODO: BUSCAR LA DE VERDAD next_check: undefined, // Que se haga instantaneamente al ser la primera - operation_type: "terminate" + operation_type: "suspend" } const taskCreated = await this.pauseRepository.addTask(newTask) From a27e4b30d26a336cebc03b972a7a62782ac424c6 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 8 Apr 2026 13:48:57 +0200 Subject: [PATCH 15/41] Cron completo y mejora de logs --- .../1.2.0_Cola-pausa-cancelacion.sql | 10 +- .../aplication/Sim.controller.test.ts | 5 +- .../aplication/Sim.controller.ts | 1 + .../aplication/Sim.usecases.ts | 68 ++++++-- .../PauseCancelTaskRepository.test.ts | 26 ++- .../PauseCancelTaskRepository.ts | 10 +- .../tasks/check_pause_terminate.ts | 157 ++++++++++++++++++ .../tasks/volcado_lineas.ts | 3 +- packages/sim-shared/domain/objeniousLine.ts | 2 +- .../ObjeniousOperationRepository.ts | 10 +- 10 files changed, 261 insertions(+), 31 deletions(-) create mode 100644 packages/sim-objenious-cron/tasks/check_pause_terminate.ts diff --git a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql index 63bdba7..9a39e62 100644 --- a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql +++ b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql @@ -4,7 +4,12 @@ * "Test" */ -CREATE TYPE SUSPENDTERMINATE AS ENUM ('suspend','terminate'); + +DO $$ BEGIN + CREATE TYPE SUSPENDTERMINATE AS ENUM ('suspend','terminate'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; CREATE TABLE IF NOT EXISTS pause_cancel_tasks ( id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, @@ -15,7 +20,8 @@ CREATE TABLE IF NOT EXISTS pause_cancel_tasks ( next_check TIMESTAMPTZ, -- Si se ha comprobado se asignará la siguiente fecha de revision completed_date TIMESTAMPTZ, -- Cuando se ha completado, para bien o mal. - error TEXT + error TEXT, + actionData JSONB -- datos de la operacion original. ); -- Indice de las tareas que no han terminado diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts index f2f3da9..facacfc 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts @@ -9,6 +9,7 @@ import { httpInstance } from "#config/httpClient.config.js"; import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"; import { PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js"; import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js"; +import { ActionData } from "#domain/DTOs/objeniousapi.js"; describe("SimController Integration Tests (Real UseCases)", () => { let eventBusMock: any; @@ -31,13 +32,14 @@ describe("SimController Integration Tests (Real UseCases)", () => { ); const orderRepository = new OrderRepository(postgrClient); const pauseRepository = new PauseCancelTaskRepository(postgrClient); - useCases = new SimUseCases({ httpClient: httpInstance, operationRepository: operationRepository, orderRepository: orderRepository, pauseRepository: pauseRepository }); + // @ts-expect-error + useCases.findActivationDate = async (data: ActionData) => new Date() controller = new SimController(eventBusMock as unknown as EventBus, useCases); }); @@ -70,6 +72,7 @@ describe("SimController Integration Tests (Real UseCases)", () => { const handler = controller.suspend(); await handler(msg); + console.log("Nack: ", eventBusMock.nack.mock.callCount()) // Verify that it reached the stage_suspend logic (which adds to pauseRepository) // We can query the DB or check if ACK was called assert.strictEqual(eventBusMock.ack.mock.callCount(), 1, "Message should be ACKed on success"); diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index ca103a0..92e445c 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -185,6 +185,7 @@ export class SimController { } const useCaseRes = await this.tryUseCase(msg, this.useCases.stage_suspend(suspendData)) + console.log("res::", useCaseRes) /* const res = await this.tryUseCase(msg, this.useCases.suspend(actionData)) */ diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 0048a7a..355aa41 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -6,6 +6,7 @@ import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } import assert from "node:assert" import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js" import { CreatePauseCancelTaskDTO, PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js" +import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js" // TODO: // - Pasar a un archivo de DTOs @@ -13,13 +14,13 @@ import { CreatePauseCancelTaskDTO, PauseCancelTaskRepository } from "#adapters/P export class SimUseCases { private readonly httpClient: HttpClient - private readonly objeniousRepository: OperationsRepositoryPort + private readonly objeniousRepository: ObjeniousOperationsRepository private readonly orderRepository: OrderRepository private readonly pauseRepository: PauseCancelTaskRepository constructor(args: { httpClient: HttpClient, - operationRepository: OperationsRepositoryPort, + operationRepository: ObjeniousOperationsRepository, orderRepository: OrderRepository, pauseRepository: PauseCancelTaskRepository }) { @@ -243,31 +244,64 @@ export class SimUseCases { }) } + /** + * Metodo muy especifico para obtener la fecha e activacion o en su defecto + * la actual para aber cuando se va a completar el periodo de test de una linea + */ + private async findActivationDate(actionData: ActionData) { + const iccid = actionData.identifier.identifiers + const lineData = await this.objeniousRepository.getLinesAPI("ICCID", iccid) + let activationDate = new Date() + // Si no se pueden sacar datos de la linea guardo momentaneamente el error + // pero no se cancela la operacion, el error puede ser de objenious y no nos + // puede afectar + if (lineData.error != undefined) { + console.error(lineData.error) + } else { + const activationDateStr = lineData.data[0].status.activationDate + if (activationDateStr != undefined && activationDateStr != "") { + activationDate = new Date(activationDateStr) + } + } + return activationDate + } + + /** * Paso previo a la suspension para evitar errores cuando el billing es test */ public stage_suspend(suspendData: ActionData): () => Promise> { return async (): Promise> => { const correlation_id = suspendData.correlation_id + const iccid = suspendData.identifier.identifiers - const newTask: CreatePauseCancelTaskDTO = { - iccid: suspendData.identifier.identifiers[0], - activation_date: new Date(), // TODO: BUSCAR LA DE VERDAD - next_check: undefined, // Que se haga instantaneamente al ser la primera - operation_type: "suspend" - } - - const taskCreated = await this.pauseRepository.addTask(newTask) - - // Caso que la task no se pueda crear en la BDD - if (taskCreated.error != undefined) { - console.error("[Sim.usecases]", taskCreated.error) + const fail = (error: string) => { + console.error("[Sim.usecases]", error) if (correlation_id != undefined) { this.orderRepository.updateOrder({ correlation_id: correlation_id, new_status: "failed" }) } + } + + console.log("Preactivationdate", suspendData) + const activationDate = await this.findActivationDate(suspendData) + console.log("ActivationDate", activationDate) + + const newTask: CreatePauseCancelTaskDTO = { + iccid: iccid[0], + activation_date: activationDate, + next_check: undefined, // Que se haga instantaneamente al ser la primera + operation_type: "suspend", + actionData: suspendData + } + + const taskCreated = await this.pauseRepository.addTask(newTask) + + // Caso que la task no se pueda crear en la BDD + if (taskCreated.error != undefined) { + fail(taskCreated.error) return { error: taskCreated.error } @@ -294,11 +328,13 @@ export class SimUseCases { return async (): Promise> => { const correlation_id = terminateData.correlation_id + const activationDate = await this.findActivationDate(terminateData) const newTask: CreatePauseCancelTaskDTO = { iccid: terminateData.identifier.identifiers[0], - activation_date: new Date(), // TODO: BUSCAR LA DE VERDAD + activation_date: activationDate, next_check: undefined, // Que se haga instantaneamente al ser la primera - operation_type: "terminate" + operation_type: "terminate", + actionData: terminateData } const taskCreated = await this.pauseRepository.addTask(newTask) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts index 4facb2c..9640031 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts @@ -7,7 +7,15 @@ const testTask: CreatePauseCancelTaskDTO = { iccid: "1234", operation_type: "suspend", activation_date: new Date(), - next_check: new Date() + next_check: new Date(), + actionData: { + dueDate: new Date().toString(), + correlation_id: "12223", + identifier: { + identifiers: ["1234"], + identifierType: "ICCID" + } + } } describe("Test PauseCancelTaskRepository - DB", () => { @@ -31,7 +39,7 @@ describe("Test PauseCancelTaskRepository - DB", () => { const created = await pauseRepo.addTask(testTask) assert.ok(created != undefined, "A value must be returned always") assert.ok(created.error == undefined, "Should not return a error") - assert.ok(created.data != undefined, "Data mus be returned") + assert.ok(created.data != undefined, "Data must be returned") createdIds.push(created.data.id) }) @@ -43,7 +51,7 @@ describe("Test PauseCancelTaskRepository - DB", () => { assert.ok(updated != undefined, "A value must be returned always") assert.ok(updated.error == undefined, "Should not return a error") - assert.ok(updated.data != undefined, "Data mus be returned") + assert.ok(updated.data != undefined, "Data must be returned") }) it("Should finish a existing task", async () => { @@ -54,6 +62,16 @@ describe("Test PauseCancelTaskRepository - DB", () => { assert.ok(finish != undefined, "A value must be returned always") assert.ok(finish.error == undefined, "Should not return a error") - assert.ok(finish.data != undefined, "Data mus be returned") + assert.ok(finish.data != undefined, "Data must be returned") + }) + + it("Should get at least 1 pending task", async () => { + const pending = await pauseRepo.getPending() + + assert.ok(pending != undefined, "A value must be returned always") + assert.ok(pending.error == undefined, "Should not return a error") + assert.ok(pending.data != undefined, "Data must be returned") + + console.log("--> ", pending.data) }) }) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts index b861c7d..30ee561 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts @@ -2,6 +2,7 @@ import { Result } from "sim-shared/domain/Result.js"; import { QueryResult } from "pg"; import { PgClient } from "sim-shared/infrastructure/PgClient.js"; import { AxiosError } from "axios"; +import { ActionData } from "#domain/DTOs/objeniousapi.js"; export type PauseCancelTask = { id: number; @@ -12,9 +13,10 @@ export type PauseCancelTask = { next_check?: Date | null; completed_date?: Date | null; error?: string | null; + actionData: ActionData } -export type CreatePauseCancelTaskDTO = Pick +export type CreatePauseCancelTaskDTO = Pick export type UpdatePauseCancelTaskDTO = Pick export type FinishPauseCancelTaskDTO = Pick @@ -55,12 +57,12 @@ export class PauseCancelTaskRepository { public async addTask(task: CreatePauseCancelTaskDTO): Promise> { const sql = ` - INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked, operation_type) - VALUES ($1, $2, $3, now(), $4) + INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked, operation_type, actionData) + VALUES ($1, $2, $3, now(), $4, $5) RETURNING *; `; try { - const values = [task.iccid, task.activation_date, task.next_check, task.operation_type]; + const values = [task.iccid, task.activation_date, task.next_check, task.operation_type, JSON.stringify(task.actionData)]; const res: QueryResult = await this.pgClient.query(sql, values); return { data: res.rows[0] diff --git a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts new file mode 100644 index 0000000..7f85fa5 --- /dev/null +++ b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts @@ -0,0 +1,157 @@ +import { ObjeniousLine } from "sim-shared/domain/objeniousLine.js"; +import { PauseCancelTaskRepository } from "sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.js"; +import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js"; +import { SimUseCases } from "sim-consumidor-objenious/aplication/Sim.usecases.js"; +import { OrderRepository } from "packages/sim-shared/infrastructure/OrderRepository.js"; + +const logger = +{ + log: (...data: any[]) => console.log("[i] [TaskPauseTerminate]", data), + error: (...data: any[]) => console.error("[x] [TaskPauseTerminate] ", data), +} + + +export class TaskPauseTerminate { + constructor( + private readonly objeniousRepo: ObjeniousOperationsRepository, + private readonly pauseRepo: PauseCancelTaskRepository, + private readonly simUsecases: SimUseCases, + private readonly orderRepo: OrderRepository + ) { + } + + public async run() { + const finError = (err: any) => { + logger.error("Finalizado con errores proceso de comprobacion de lineas en pausa o canceladas") + logger.error(err) + } + try { + logger.log("Iniciando proceso de comprobacion de lineas en pausa o canceladas") + + // 1. Se comprueba cuantas peticiones hay qye revisar + const peticionesRevisar = await this.pauseRepo.getPending() + + if (peticionesRevisar.error != undefined) { + finError(peticionesRevisar.error) + return 1; + } + + logger.log(`Se van a revisar ${peticionesRevisar.data?.length} peticiones`) + + // 2. Se comprueba que alguna de las lineas haya dejado de estar en estado de test + const iccids = peticionesRevisar.data.map(e => e.iccid) + const lineasActualizadas: ObjeniousLine[] = [] + + const lineGenerator = this.objeniousRepo.getLinesByStatusAPI({ + iccids: iccids + }) + + let lines = await lineGenerator.next() + + if (lines.value.error != undefined || lines.value.data == undefined) { + logger.error("Error cargando las lineas", lines.value.error) + finError(lines.value.error) + return 1; + } else { + lineasActualizadas.push(...lines.value.data) + } + + while (!lines.done) { + if (lines.value.error != undefined || lines.value.data == undefined) { + logger.error("Error cargando las lineas", lines.value.error) + finError(lines.value.error) + return 1; + } else { + lineasActualizadas.push(...lines.value.data) + } + + lines = await lineGenerator.next() + } + + // 3. Se separan las lineas que se tienen que actualizar al no ser test + // y las que se tienen que reencolar al ser test + const lineasNoTest = lineasActualizadas.filter(e => e.status.billingStatus != "TEST") + const lineasTest = lineasActualizadas.filter(e => e.status.billingStatus == "TEST") + + // 4. Las lineas de test se reencolan + // El proximo reintento es en 1 dia + const proximoReintento = new Date() + proximoReintento.setDate(new Date().getDate() + 1) + + // 5. Reintentos en 1 dia + for (const linea of lineasTest) { + const lineaId = peticionesRevisar.data + .find(e => e.iccid == linea.identifier.iccid)?.id + + if (lineaId == undefined) continue; // Esto puede ser un problema si se generaliza + + this.pauseRepo.updateTask({ + id: lineaId, + next_check: proximoReintento + }) + } + + // 6. Operaciones de pausa/cancelacion definitiva + for (const linea of lineasNoTest) { + const operacion = peticionesRevisar.data + .find(e => e.iccid == linea.identifier.iccid) + + if (operacion == undefined) continue; + const dueDate = new Date() + dueDate.setMinutes(new Date().getMinutes() + 15) + + const operacionTipo = operacion.operation_type + const actionData = operacion.actionData + const correlation_id = operacion.actionData.correlation_id + actionData.dueDate = dueDate.toString() + + if (linea.status.billingStatus == "ACTIVATED") { + let exito = false; + let result = null; + // IMPORTANTE COMRPOBAR EL DUE DATE + switch (operacionTipo) { + case "suspend": + result = await this.simUsecases.suspend(actionData)() + break; + case "terminate": + result = await this.simUsecases.terminate(actionData)() + break; + default: + break; + } + + if (result == undefined) { + logger.error("Operacion desconocida", operacion) + } else if (result?.error != undefined) { + // error usecase + logger.error(result.error) + await this.pauseRepo.finishTask({ + id: operacion.id, + error: result.error + }) + if (correlation_id != undefined) + await this.orderRepo.errorOrder({ + correlation_id: correlation_id, + status: "dlx", + reason: result.error + }) + } else { + // ok + await this.pauseRepo.finishTask({ id: operacion.id }) + if (correlation_id != undefined) + await this.orderRepo.finishOrder({ correlation_id }) + } + } + + // TODO: SUSPENDED Y TERMINATED + } + + + logger.log("Finalizado con exito proceso de comprobacion de lineas en pausa o canceladas") + } catch (e) { + finError(e) + } + + return 0 + } +} diff --git a/packages/sim-objenious-cron/tasks/volcado_lineas.ts b/packages/sim-objenious-cron/tasks/volcado_lineas.ts index bea139a..d06eb87 100644 --- a/packages/sim-objenious-cron/tasks/volcado_lineas.ts +++ b/packages/sim-objenious-cron/tasks/volcado_lineas.ts @@ -1,6 +1,6 @@ import { lineToCreateLineDto, ObjeniousLine } from "sim-shared/domain/objeniousLine.js"; import { ObjeniousLinesRepository } from "../infranstructure/ObjeniousLinesRepository.js"; -import { ObjeniousOperationsRepository } from "packages/sim-shared/infrastructure/ObjeniousOperationRepository.js"; +import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js"; export class TaskVolcadoLineas { constructor( @@ -40,7 +40,6 @@ export class TaskVolcadoLineas { await this.saveLines(lines.value.data) while (!lines.done) { - console.log() lines = await linesIterator.next() if (lines.value.error != undefined || lines.value.data == undefined) { console.error("[x] Error cargando las lineas a volcar", lines.value.error) diff --git a/packages/sim-shared/domain/objeniousLine.ts b/packages/sim-shared/domain/objeniousLine.ts index c04eb30..cf48f3b 100644 --- a/packages/sim-shared/domain/objeniousLine.ts +++ b/packages/sim-shared/domain/objeniousLine.ts @@ -83,7 +83,7 @@ export type ObjeniousLine = { commercialStatus: string, //"test", commercialStatusDate: string, //"2026-03-17T11:41:01.493+00:00", networkStatus: string, // "ACTIVATED", - billingStatus: string, //"TEST", + billingStatus: "ACTIVATED" | "SUSPENDED" | "CANCELED" | "TEST", billingStatusChangeDate: string | null, // "2026-03-17T11:01:00.276+00:00", billingActivationDate: string | null //, createdDate: string | null,//"2026-01-30T01:50:02.060+00:00" diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index a48523a..68d0009 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -16,6 +16,7 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { /** * Consulta el estado de una o mas lineas directamente a la API de Objenious + * TODO: No hay paginacion como en getLinesByStatusAPI */ public async getLinesAPI( identifierType: "ICCID" | "IMSI" | "IMEI" | "MSISDN" | "REFERENCE", @@ -59,7 +60,8 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { public async * getLinesByStatusAPI(args?: { pageSize?: number, pageNumber?: number, - status?: string + status?: string, + iccids?: string[] }): AsyncGenerator, Result, any> { const path = "/lines" @@ -70,6 +72,12 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { const params: Record = {} + // Si se va a filtrar por iccids especificamente, en un futuro habra que ampliar el tipo de filtros + if (args?.iccids != undefined) { + params["identifier.identifierType"] = "ICCID" + params["identifier.identifiers"] = args.iccids.toString() + } + const loadNextLine = async (page: number): Promise> => { if (args?.status != undefined) params["simStatus"] = args.status params["pageSize"] = pageSize From a9589f578b05686b2253733d2b61db45cc5ad713 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 8 Apr 2026 14:47:57 +0200 Subject: [PATCH 16/41] Solucionado cierrre de pool para test --- .../aplication/Sim.controller.test.ts | 7 +++++-- .../sim-consumidor-objenious/aplication/Sim.controller.ts | 1 - .../sim-consumidor-objenious/aplication/Sim.usecases.ts | 2 -- packages/sim-shared/infrastructure/OrderRepository.ts | 5 ++++- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts index facacfc..b408723 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.test.ts @@ -4,7 +4,7 @@ import { SimController } from "./Sim.controller.js"; import { EventBus } from "sim-shared/domain/EventBus.port.js"; import { SimUseCases } from "./Sim.usecases.js"; import { ConsumeMessage } from "amqplib"; -import { postgrClient } from "#config/postgreConfig.js"; +import { postgrClient, pgPool } from "#config/postgreConfig.js"; import { httpInstance } from "#config/httpClient.config.js"; import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"; import { PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js"; @@ -56,6 +56,10 @@ describe("SimController Integration Tests (Real UseCases)", () => { } as unknown as ConsumeMessage; }; + after(async () => { + await pgPool.end(); + }); + describe("suspend", () => { it("should call stage_suspend and interact with DB and EventBus", async () => { const iccid = "test-iccid-suspend-" + Date.now(); @@ -72,7 +76,6 @@ describe("SimController Integration Tests (Real UseCases)", () => { const handler = controller.suspend(); await handler(msg); - console.log("Nack: ", eventBusMock.nack.mock.callCount()) // Verify that it reached the stage_suspend logic (which adds to pauseRepository) // We can query the DB or check if ACK was called assert.strictEqual(eventBusMock.ack.mock.callCount(), 1, "Message should be ACKed on success"); diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index 92e445c..ca103a0 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -185,7 +185,6 @@ export class SimController { } const useCaseRes = await this.tryUseCase(msg, this.useCases.stage_suspend(suspendData)) - console.log("res::", useCaseRes) /* const res = await this.tryUseCase(msg, this.useCases.suspend(actionData)) */ diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 355aa41..ccb822c 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -285,9 +285,7 @@ export class SimUseCases { } } - console.log("Preactivationdate", suspendData) const activationDate = await this.findActivationDate(suspendData) - console.log("ActivationDate", activationDate) const newTask: CreatePauseCancelTaskDTO = { iccid: iccid[0], diff --git a/packages/sim-shared/infrastructure/OrderRepository.ts b/packages/sim-shared/infrastructure/OrderRepository.ts index b2e965a..757e9e8 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.ts @@ -191,6 +191,8 @@ export class OrderRepository { const orderId = currentOrderResult.data?.id if (orderId == undefined) { + await client.query("ROLLBACK") + client.release() return { error: "El order a actualizar no existe " + idType + ": " + idValue } @@ -261,7 +263,6 @@ export class OrderRepository { return updatedOrder } - public async finishOrder(args: FinishOrderDTO) { const client = await this.pgClient.connect(); assert((args.id != undefined) != (args.correlation_id != undefined)) @@ -281,6 +282,8 @@ export class OrderRepository { const orderId = currentOrderResult.data?.id if (orderId == undefined) { + await client.query("ROLLBACK") + client.release() return { error: "El order a actualizar no existe " + idType + ": " + idValue } From 7ff3f13af4f5934a4dd4761266125ebf9c95042d Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 8 Apr 2026 17:37:47 +0200 Subject: [PATCH 17/41] Funcionan las suspensiones --- docs/sim-api/Pause.bru | 2 +- docs/sim-api/environments/local.bru | 1 + docs/sim-api/environments/prod.bru | 1 + .../aplication/Sim.controller.ts | 2 +- .../aplication/Sim.usecases.ts | 12 +++++++-- .../aplication/Sim.controller.ts | 1 - packages/sim-objenious-cron/index.ts | 25 ++++++++++++++++++- .../tasks/check_pause_terminate.ts | 2 +- .../ObjeniousOperationRepository.ts | 4 +-- 9 files changed, 41 insertions(+), 9 deletions(-) diff --git a/docs/sim-api/Pause.bru b/docs/sim-api/Pause.bru index cef1ed7..bcb3b8e 100644 --- a/docs/sim-api/Pause.bru +++ b/docs/sim-api/Pause.bru @@ -15,7 +15,7 @@ params:query { } body:form-urlencoded { - iccid: 8933201125065160414 + iccid: 8933201125068886692 } settings { diff --git a/docs/sim-api/environments/local.bru b/docs/sim-api/environments/local.bru index 79faa0b..fab4907 100644 --- a/docs/sim-api/environments/local.bru +++ b/docs/sim-api/environments/local.bru @@ -1,3 +1,4 @@ vars { baseurl: http://localhost:3000 } +color: #2E8A54 diff --git a/docs/sim-api/environments/prod.bru b/docs/sim-api/environments/prod.bru index 1ef2e33..d6e2471 100644 --- a/docs/sim-api/environments/prod.bru +++ b/docs/sim-api/environments/prod.bru @@ -1,3 +1,4 @@ vars { baseurl: https://sf-sims.savefamilygps.net } +color: #CE4F3B diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index ca103a0..800cf98 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -164,6 +164,7 @@ export class SimController { public suspend() { return async (msg: ConsumeMessage) => { let msgData; + console.log("Consumiendo susension", msg) try { msgData = this.validateMsg(msg) as SimEvents.pause } catch (e) { @@ -183,7 +184,6 @@ export class SimController { 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)) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index ccb822c..13c0b1a 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -255,6 +255,7 @@ export class SimUseCases { // Si no se pueden sacar datos de la linea guardo momentaneamente el error // pero no se cancela la operacion, el error puede ser de objenious y no nos // puede afectar + console.log("LineData", lineData.data) if (lineData.error != undefined) { console.error(lineData.error) } else { @@ -272,6 +273,7 @@ export class SimUseCases { */ public stage_suspend(suspendData: ActionData): () => Promise> { return async (): Promise> => { + console.log("Suspend action data", suspendData) const correlation_id = suspendData.correlation_id const iccid = suspendData.identifier.identifiers @@ -285,8 +287,14 @@ export class SimUseCases { } } - const activationDate = await this.findActivationDate(suspendData) - + let activationDate; + try { + activationDate = await this.findActivationDate(suspendData) + } catch (e) { + return { + error: String(e) + } + } const newTask: CreatePauseCancelTaskDTO = { iccid: iccid[0], activation_date: activationDate, diff --git a/packages/sim-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index 0c69c8b..9afe780 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -36,7 +36,6 @@ export class SimController { }) { return async (req: Request, res: Response) => { const body = req.body - // 1. Validacion del body if (args.validator != undefined) { const validationResult = args.validator.validate(body) diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index 9153d39..9db6a95 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -8,6 +8,9 @@ import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js" import { TaskVolcadoLineas } from "./tasks/volcado_lineas.js" import { ObjeniousLinesRepository } from "./infranstructure/ObjeniousLinesRepository.js" import { postgresClientIntranet } from "./config/intranetPostgresConfig.js" +import { PauseCancelTaskRepository } from "packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.js" +import { PauseTerminateTask } from "./tasks/check_pause_terminate.js" +import { SimUseCases } from "packages/sim-consumidor-objenious/aplication/Sim.usecases.js" async function startCron() { const commonSettings = { @@ -44,6 +47,21 @@ async function startCron() { objeniosRepo ) + const pauseRepo = new PauseCancelTaskRepository(pgClient) + const simUsecases = new SimUseCases({ + httpClient: httpClient, + operationRepository: operationRepository, + orderRepository: orderRepository, + pauseRepository: pauseRepo + }) + + const pauseTask = new PauseTerminateTask( + objeniosRepo, + pauseRepo, + simUsecases, + orderRepository + ) + const PERIODO_PETICIONES = 10 * 60 * 1000 const interval = setInterval(async () => { try { @@ -62,7 +80,12 @@ async function startCron() { } }, PERIODO_VOLCADO) - await volcadoLineasTask.loadLines() + + //await pauseTask.run() + const PERIODO_CANCELACIONES = 24 * 60 * 60 * 1000; + const clacelacionesInterval = setInterval(async () => { + await pauseTask.run() + }, PERIODO_CANCELACIONES) } diff --git a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts index 7f85fa5..6d9da8b 100644 --- a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts +++ b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts @@ -11,7 +11,7 @@ const logger = } -export class TaskPauseTerminate { +export class PauseTerminateTask { constructor( private readonly objeniousRepo: ObjeniousOperationsRepository, private readonly pauseRepo: PauseCancelTaskRepository, diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index 68d0009..4db3951 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -36,7 +36,7 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { "identifier.identifiers": identifiers.toString() } - const req = this.http.client.get(path, { + const req = this.http.client.get(path, { params: params }) @@ -48,7 +48,7 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { } } - const lines = res.data.data + const lines = res.data.data.content return { data: lines From 5ea5939e3af930917ec5ae37e14479e3c52b7fce Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 9 Apr 2026 09:08:11 +0200 Subject: [PATCH 18/41] Bug de finaliazacion de tareas erroneas --- packages/sim-objenious-cron/tasks/check_objenious_request.ts | 3 ++- .../sim-shared/infrastructure/ObjeniousOperationRepository.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index 05ba66b..ed33bb8 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -3,10 +3,11 @@ import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"; import axios from "axios"; import { IOperationsRepository, Objenious, ObjeniousOperation, ObjeniousOperationChange, StatusEnum } from "sim-shared/domain/operationsRepository.port.js"; import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"; +import { ObjeniousOperationsRepository } from "packages/sim-shared/infrastructure/ObjeniousOperationRepository.js"; export class CheckObjeniousRequests { constructor( - private readonly operationsRepository: IOperationsRepository, + private readonly operationsRepository: ObjeniousOperationsRepository, private readonly orderRepository: OrderRepository, private readonly httpClient: HttpClient ) { diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index 4db3951..5010326 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -171,7 +171,7 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { request_id = COALESCE($4, request_id), mass_action_id = COALESCE($5, mass_action_id), last_change_date = now() at time zone 'utc', - end_date = CASE WHEN $2 IN ('finished') THEN now() at time zone 'utc' ELSE end_date END, + end_date = CASE WHEN $2 IN ('finished','error') THEN now() at time zone 'utc' ELSE end_date END, objenious_status = $6 WHERE id = $1`; From 047669bab25a60d3597a3bac5637fb0d1d1fb6d6 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 9 Apr 2026 11:53:49 +0200 Subject: [PATCH 19/41] Acabados de corregir bugs --- .../aplication/Sim.controller.ts | 1 - .../aplication/Sim.usecases.ts | 5 +- .../PauseCancelTaskRepository.test.ts | 11 +- .../PauseCancelTaskRepository.ts | 6 +- packages/sim-objenious-cron/index.ts | 4 +- .../tasks/check_pause_terminate.ts | 108 ++++++++++++------ 6 files changed, 80 insertions(+), 55 deletions(-) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index 800cf98..08bc354 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -164,7 +164,6 @@ export class SimController { public suspend() { return async (msg: ConsumeMessage) => { let msgData; - console.log("Consumiendo susension", msg) try { msgData = this.validateMsg(msg) as SimEvents.pause } catch (e) { diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 13c0b1a..bd92c96 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -273,7 +273,6 @@ export class SimUseCases { */ public stage_suspend(suspendData: ActionData): () => Promise> { return async (): Promise> => { - console.log("Suspend action data", suspendData) const correlation_id = suspendData.correlation_id const iccid = suspendData.identifier.identifiers @@ -300,7 +299,7 @@ export class SimUseCases { activation_date: activationDate, next_check: undefined, // Que se haga instantaneamente al ser la primera operation_type: "suspend", - actionData: suspendData + actiondata: suspendData } const taskCreated = await this.pauseRepository.addTask(newTask) @@ -340,7 +339,7 @@ export class SimUseCases { activation_date: activationDate, next_check: undefined, // Que se haga instantaneamente al ser la primera operation_type: "terminate", - actionData: terminateData + actiondata: terminateData } const taskCreated = await this.pauseRepository.addTask(newTask) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts index 9640031..4a63a3e 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts @@ -8,7 +8,7 @@ const testTask: CreatePauseCancelTaskDTO = { operation_type: "suspend", activation_date: new Date(), next_check: new Date(), - actionData: { + actiondata: { dueDate: new Date().toString(), correlation_id: "12223", identifier: { @@ -19,20 +19,14 @@ const testTask: CreatePauseCancelTaskDTO = { } describe("Test PauseCancelTaskRepository - DB", () => { - function clean() { - - } const createdIds: number[] = []; - const pauseRepo = new PauseCancelTaskRepository(postgrClient) before(() => { - }) after(() => { - }) it("Should create a task", async () => { @@ -66,12 +60,13 @@ describe("Test PauseCancelTaskRepository - DB", () => { }) it("Should get at least 1 pending task", async () => { + const created = await pauseRepo.addTask(testTask) const pending = await pauseRepo.getPending() assert.ok(pending != undefined, "A value must be returned always") assert.ok(pending.error == undefined, "Should not return a error") assert.ok(pending.data != undefined, "Data must be returned") - console.log("--> ", pending.data) + console.log("--> ", pending.data[0]) }) }) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts index 30ee561..129ea6e 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts @@ -13,10 +13,10 @@ export type PauseCancelTask = { next_check?: Date | null; completed_date?: Date | null; error?: string | null; - actionData: ActionData + actiondata: ActionData } -export type CreatePauseCancelTaskDTO = Pick +export type CreatePauseCancelTaskDTO = Pick export type UpdatePauseCancelTaskDTO = Pick export type FinishPauseCancelTaskDTO = Pick @@ -62,7 +62,7 @@ export class PauseCancelTaskRepository { RETURNING *; `; try { - const values = [task.iccid, task.activation_date, task.next_check, task.operation_type, JSON.stringify(task.actionData)]; + const values = [task.iccid, task.activation_date, task.next_check, task.operation_type, JSON.stringify(task.actiondata)]; const res: QueryResult = await this.pgClient.query(sql, values); return { data: res.rows[0] diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index 9db6a95..e325b98 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -81,8 +81,8 @@ async function startCron() { }, PERIODO_VOLCADO) - //await pauseTask.run() - const PERIODO_CANCELACIONES = 24 * 60 * 60 * 1000; + await pauseTask.run() + const PERIODO_CANCELACIONES = 60 * 60 * 1000; const clacelacionesInterval = setInterval(async () => { await pauseTask.run() }, PERIODO_CANCELACIONES) diff --git a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts index 6d9da8b..f588ef6 100644 --- a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts +++ b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts @@ -6,8 +6,8 @@ import { OrderRepository } from "packages/sim-shared/infrastructure/OrderReposit const logger = { - log: (...data: any[]) => console.log("[i] [TaskPauseTerminate]", data), - error: (...data: any[]) => console.error("[x] [TaskPauseTerminate] ", data), + log: (...data: any[]) => console.log("[i] [TaskPauseTerminate]", ...data), + error: (...data: any[]) => console.error("[x] [TaskPauseTerminate] ", ...data), } @@ -25,6 +25,10 @@ export class PauseTerminateTask { logger.error("Finalizado con errores proceso de comprobacion de lineas en pausa o canceladas") logger.error(err) } + + const finExito = () => { + logger.log("Finalizado con exito proceso de comprobacion de lineas en pausa o canceladas") + } try { logger.log("Iniciando proceso de comprobacion de lineas en pausa o canceladas") @@ -37,6 +41,11 @@ export class PauseTerminateTask { } logger.log(`Se van a revisar ${peticionesRevisar.data?.length} peticiones`) + if (peticionesRevisar.data == undefined || peticionesRevisar.data.length == 0) { + finExito() + return 0; + } + // 2. Se comprueba que alguna de las lineas haya dejado de estar en estado de test const iccids = peticionesRevisar.data.map(e => e.iccid) @@ -68,6 +77,8 @@ export class PauseTerminateTask { lines = await lineGenerator.next() } + console.log("Cargado: ", lineasActualizadas) + // 3. Se separan las lineas que se tienen que actualizar al no ser test // y las que se tienen que reencolar al ser test const lineasNoTest = lineasActualizadas.filter(e => e.status.billingStatus != "TEST") @@ -101,53 +112,74 @@ export class PauseTerminateTask { dueDate.setMinutes(new Date().getMinutes() + 15) const operacionTipo = operacion.operation_type - const actionData = operacion.actionData - const correlation_id = operacion.actionData.correlation_id - actionData.dueDate = dueDate.toString() + const actionData = operacion.actiondata + const correlation_id = operacion.actiondata.correlation_id + actionData.dueDate = dueDate.toISOString() - if (linea.status.billingStatus == "ACTIVATED") { - let exito = false; - let result = null; - // IMPORTANTE COMRPOBAR EL DUE DATE - switch (operacionTipo) { - case "suspend": - result = await this.simUsecases.suspend(actionData)() - break; - case "terminate": - result = await this.simUsecases.terminate(actionData)() - break; - default: - break; - } + switch (linea.status.billingStatus) { + case "ACTIVATED": + let exito = false; + let result = null; + // IMPORTANTE COMRPOBAR EL DUE DATE + switch (operacionTipo) { + case "suspend": + result = await this.simUsecases.suspend(actionData)() + break; + case "terminate": + result = await this.simUsecases.terminate(actionData)() + break; + default: + break; + } - if (result == undefined) { - logger.error("Operacion desconocida", operacion) - } else if (result?.error != undefined) { - // error usecase - logger.error(result.error) + if (result == undefined) { + logger.error("Operacion desconocida", operacion) + } else if (result?.error != undefined) { + // error usecase + logger.error(result.error) + await this.pauseRepo.finishTask({ + id: operacion.id, + error: result.error + }) + if (correlation_id != undefined) + await this.orderRepo.errorOrder({ + correlation_id: correlation_id, + status: "dlx", + reason: result.error + }) + } else { + // ok + await this.pauseRepo.finishTask({ id: operacion.id }) + if (correlation_id != undefined) + await this.orderRepo.finishOrder({ correlation_id }) + } + + break; + case "CANCELED": await this.pauseRepo.finishTask({ id: operacion.id, - error: result.error + error: "billingStatus is CANCELED" }) if (correlation_id != undefined) - await this.orderRepo.errorOrder({ - correlation_id: correlation_id, - status: "dlx", - reason: result.error - }) - } else { - // ok - await this.pauseRepo.finishTask({ id: operacion.id }) + await this.orderRepo.finishOrder({ correlation_id }) + break; + case "SUSPENDED": + await this.pauseRepo.finishTask({ + id: operacion.id, + error: "billingStatus is SUSPENDED" + }) if (correlation_id != undefined) await this.orderRepo.finishOrder({ correlation_id }) - } - } + break; + case "TEST": + // No puede ser + default: + logger.error("billingStatus desconocido", linea.status.billingStatus) - // TODO: SUSPENDED Y TERMINATED + } } - - logger.log("Finalizado con exito proceso de comprobacion de lineas en pausa o canceladas") + finExito() } catch (e) { finError(e) } From 031f5d5cf0cbc6f3b0744f81cda5aae3fa93a015 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 9 Apr 2026 11:59:06 +0200 Subject: [PATCH 20/41] nombre de columna con mayus --- .../database/migrations/1.2.0_Cola-pausa-cancelacion.sql | 2 +- .../sim-consumidor-objenious/aplication/Sim.usecases.ts | 4 ++-- .../infrastructure/PauseCancelTaskRepository.test.ts | 2 +- .../infrastructure/PauseCancelTaskRepository.ts | 6 +++--- packages/sim-objenious-cron/tasks/check_pause_terminate.ts | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql index 9a39e62..3d0791e 100644 --- a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql +++ b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql @@ -21,7 +21,7 @@ CREATE TABLE IF NOT EXISTS pause_cancel_tasks ( completed_date TIMESTAMPTZ, -- Cuando se ha completado, para bien o mal. error TEXT, - actionData JSONB -- datos de la operacion original. + action_data JSONB -- datos de la operacion original. ); -- Indice de las tareas que no han terminado diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index bd92c96..119b0f9 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -299,7 +299,7 @@ export class SimUseCases { activation_date: activationDate, next_check: undefined, // Que se haga instantaneamente al ser la primera operation_type: "suspend", - actiondata: suspendData + action_data: suspendData } const taskCreated = await this.pauseRepository.addTask(newTask) @@ -339,7 +339,7 @@ export class SimUseCases { activation_date: activationDate, next_check: undefined, // Que se haga instantaneamente al ser la primera operation_type: "terminate", - actiondata: terminateData + action_data: terminateData } const taskCreated = await this.pauseRepository.addTask(newTask) diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts index 4a63a3e..a1e48e4 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.test.ts @@ -8,7 +8,7 @@ const testTask: CreatePauseCancelTaskDTO = { operation_type: "suspend", activation_date: new Date(), next_check: new Date(), - actiondata: { + action_data: { dueDate: new Date().toString(), correlation_id: "12223", identifier: { diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts index 129ea6e..219dac4 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts @@ -13,10 +13,10 @@ export type PauseCancelTask = { next_check?: Date | null; completed_date?: Date | null; error?: string | null; - actiondata: ActionData + action_data: ActionData } -export type CreatePauseCancelTaskDTO = Pick +export type CreatePauseCancelTaskDTO = Pick export type UpdatePauseCancelTaskDTO = Pick export type FinishPauseCancelTaskDTO = Pick @@ -62,7 +62,7 @@ export class PauseCancelTaskRepository { RETURNING *; `; try { - const values = [task.iccid, task.activation_date, task.next_check, task.operation_type, JSON.stringify(task.actiondata)]; + const values = [task.iccid, task.activation_date, task.next_check, task.operation_type, JSON.stringify(task.action_data)]; const res: QueryResult = await this.pgClient.query(sql, values); return { data: res.rows[0] diff --git a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts index f588ef6..a43a832 100644 --- a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts +++ b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts @@ -112,8 +112,8 @@ export class PauseTerminateTask { dueDate.setMinutes(new Date().getMinutes() + 15) const operacionTipo = operacion.operation_type - const actionData = operacion.actiondata - const correlation_id = operacion.actiondata.correlation_id + const actionData = operacion.action_data + const correlation_id = operacion.action_data.correlation_id actionData.dueDate = dueDate.toISOString() switch (linea.status.billingStatus) { From 48d387a8da144e8a51a5fda4ff00ec5208bd1d29 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 9 Apr 2026 12:10:40 +0200 Subject: [PATCH 21/41] Migraciones antes de lanzar start --- deployment/develop/docker/entrypoint.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/deployment/develop/docker/entrypoint.sh b/deployment/develop/docker/entrypoint.sh index 416704d..2fdb3cb 100644 --- a/deployment/develop/docker/entrypoint.sh +++ b/deployment/develop/docker/entrypoint.sh @@ -1,4 +1,6 @@ #!/bin/sh cd /home -cd /home/node/app && yarn start +cd /home/node/app +yarn migrate +yarn start From d9854a12a80d85ef9d8a47dea50fdd1dc2de39ee Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 9 Apr 2026 12:31:06 +0200 Subject: [PATCH 22/41] Version de migrate --- .env | 2 +- package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.env b/.env index 681f736..ecc3477 100644 --- a/.env +++ b/.env @@ -20,7 +20,7 @@ POSTGRES_DB=postgres POSTGRES_DATABASE=postgres POSTGRES_PORT=5433 POSTGRES_USER=postgres -POSTGRES_PASSWORD=1234 +POSTGRES_PASSWORD='1234' # Para el postgres local para generar el script de resultado de migraciones PGHOST=localhost diff --git a/package.json b/package.json index d40554c..2a43600 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "migrate": "yarn db-migrate -e .env -m deployment/database/migrations -t 99.0.0" }, "dependencies": { - "@sf-alvar/db-migrate": "1.0.3", + "@sf-alvar/db-migrate": "1.0.6", "@tsconfig/node22": "^22.0.5", "amqp-connection-manager": "^5.0.0", "amqplib": "^0.10.9", diff --git a/yarn.lock b/yarn.lock index 19cf892..d670587 100644 --- a/yarn.lock +++ b/yarn.lock @@ -452,15 +452,15 @@ __metadata: languageName: node linkType: hard -"@sf-alvar/db-migrate@npm:1.0.3": - version: 1.0.3 - resolution: "@sf-alvar/db-migrate@npm:1.0.3::__archiveUrl=https%3A%2F%2Fgit.savefamilygps.net%2Fapi%2Fpackages%2FSaveFamily%2Fnpm%2F%2540sf-alvar%252Fdb-migrate%2F-%2F1.0.3%2Fdb-migrate-1.0.3.tgz" +"@sf-alvar/db-migrate@npm:1.0.6": + version: 1.0.6 + resolution: "@sf-alvar/db-migrate@npm:1.0.6::__archiveUrl=https%3A%2F%2Fgit.savefamilygps.net%2Fapi%2Fpackages%2FSaveFamily%2Fnpm%2F%2540sf-alvar%252Fdb-migrate%2F-%2F1.0.6%2Fdb-migrate-1.0.6.tgz" dependencies: pg: "npm:^8.18.0" yargs: "npm:^18.0.0" bin: db-migrate: lib/index.js - checksum: 10/2b5745a5ce60456fc7fee1e6a8580978a520fedd8abbbc695557847cdf2b36aa5e1d795721ad35bc151fc9373dfa023bde73d6f43ba412b17293a1822c09fe6b + checksum: 10/070f1388ff1c6fd2d24c3139d779e871bc0db94f11dd2013aa7eb5728e3c21e594bac0f4d46f8f3132391a9903cca56d5c864862c622d70f24e0db0ffcbbbf0e languageName: node linkType: hard @@ -2856,7 +2856,7 @@ __metadata: version: 0.0.0-use.local resolution: "sim-eventos@workspace:." dependencies: - "@sf-alvar/db-migrate": "npm:1.0.3" + "@sf-alvar/db-migrate": "npm:1.0.6" "@tsconfig/node22": "npm:^22.0.5" "@types/amqplib": "npm:^0.10.8" "@types/cors": "npm:^2.8.19" From e3849d8217190b5a5244b7ae5d64ccb653b84fb3 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 9 Apr 2026 12:48:36 +0200 Subject: [PATCH 23/41] Archivos de migraciones --- deployment/develop/docker/Dockerfile | 2 ++ deployment/develop/jenkinsfile.groovy | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/deployment/develop/docker/Dockerfile b/deployment/develop/docker/Dockerfile index ca0a124..3623791 100644 --- a/deployment/develop/docker/Dockerfile +++ b/deployment/develop/docker/Dockerfile @@ -8,6 +8,8 @@ RUN corepack enable COPY ./dist/packages ./packages COPY ./.yarnrc.yml ./ COPY ./docs ./docs +# Para las migraciones +COPY ./deployment ./deployment COPY ./package.json ./ diff --git a/deployment/develop/jenkinsfile.groovy b/deployment/develop/jenkinsfile.groovy index 3b69366..291e0fe 100644 --- a/deployment/develop/jenkinsfile.groovy +++ b/deployment/develop/jenkinsfile.groovy @@ -69,7 +69,6 @@ pipeline { cleanRemote: false, remoteDirectory: "$APP_REMOTE_PATH", sourceFiles: "deployment/database/**/*", - removePrefix: "deployment", ), sshTransfer( cleanRemote: false, From 70bf73b0a4b6837a260833660801deae20bf9806 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Fri, 10 Apr 2026 11:11:12 +0200 Subject: [PATCH 24/41] Error query --- docs/sim-api/Activate.bru | 2 +- .../infrastructure/PauseCancelTaskRepository.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index 0d4eda1..6417025 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125068886692 + iccid: 8933201125068890694 offer: SAVEFAMILY1 } diff --git a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts index 219dac4..e8bb1da 100644 --- a/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts +++ b/packages/sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.ts @@ -57,7 +57,7 @@ export class PauseCancelTaskRepository { public async addTask(task: CreatePauseCancelTaskDTO): Promise> { const sql = ` - INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked, operation_type, actionData) + INSERT INTO pause_cancel_tasks (iccid, activation_date, next_check, last_checked, operation_type, action_data) VALUES ($1, $2, $3, now(), $4, $5) RETURNING *; `; From 4d34308a13b3582817cabc46278049a37c797bab Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 14 Apr 2026 16:07:17 +0200 Subject: [PATCH 25/41] Bug de logueo de operacion --- docs/sim-api/Cancel.bru | 2 +- docs/sim-api/Pause.bru | 2 +- .../aplication/Sim.usecases.ts | 37 ++++++++++- .../aplication/Sim.usecases.ts | 6 +- .../infrastructure/simRoutes.http.ts | 8 +++ packages/sim-entrada-eventos/package.json | 1 + .../domain/operationsRepository.port.ts | 2 +- yarn.lock | 63 ++++++++++++++++++- 8 files changed, 111 insertions(+), 10 deletions(-) diff --git a/docs/sim-api/Cancel.bru b/docs/sim-api/Cancel.bru index 4c5584e..778e066 100644 --- a/docs/sim-api/Cancel.bru +++ b/docs/sim-api/Cancel.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125068886692 + iccid: 8933201125068887864 } settings { diff --git a/docs/sim-api/Pause.bru b/docs/sim-api/Pause.bru index bcb3b8e..4a88e4d 100644 --- a/docs/sim-api/Pause.bru +++ b/docs/sim-api/Pause.bru @@ -15,7 +15,7 @@ params:query { } body:form-urlencoded { - iccid: 8933201125068886692 + iccid: 8933201125068886700 } settings { diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 119b0f9..304d13f 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -75,13 +75,14 @@ export class SimUseCases { operation: args.operation, iccids: String(args.iccid), status: "noMassID", - request_id: response.data.requestId + request_id: response.data.requestId, + correlation_id: args.correlation_id } // TODO: Esto tiene poco sentido si la operacion ya se // tenia que haber creado en el generador this.logOperation(operation) - .then().catch(e => console.error(e)) + .then().catch(e => console.error("Error login operation", e)) if (args.correlation_id != undefined) { this.orderRepository.updateOrder({ @@ -276,6 +277,17 @@ export class SimUseCases { const correlation_id = suspendData.correlation_id const iccid = suspendData.identifier.identifiers + + const operation: ObjeniousOperation = { + operation: "suspend", + iccids: iccid[0], + status: "running", + correlation_id: correlation_id + } + + this.logOperation(operation) + .then().catch(e => console.error("Error login operation", e)) + const fail = (error: string) => { console.error("[Sim.usecases]", error) if (correlation_id != undefined) { @@ -286,6 +298,14 @@ export class SimUseCases { } } + // TODO REGISTRAR EL ORDER + if (correlation_id != undefined) { + await this.orderRepository.createOrder({ + correlation_id: correlation_id, + order_type: "pause" + }) + } + let activationDate; try { activationDate = await this.findActivationDate(suspendData) @@ -332,10 +352,11 @@ export class SimUseCases { public stage_terminate(terminateData: ActionData): () => Promise> { return async (): Promise> => { const correlation_id = terminateData.correlation_id + const iccid = terminateData.identifier.identifiers[0] const activationDate = await this.findActivationDate(terminateData) const newTask: CreatePauseCancelTaskDTO = { - iccid: terminateData.identifier.identifiers[0], + iccid: iccid, activation_date: activationDate, next_check: undefined, // Que se haga instantaneamente al ser la primera operation_type: "terminate", @@ -344,6 +365,16 @@ export class SimUseCases { const taskCreated = await this.pauseRepository.addTask(newTask) + const operation: ObjeniousOperation = { + operation: "terminate", + iccids: iccid, + status: "running", + correlation_id: correlation_id + } + + this.logOperation(operation) + .then().catch(e => console.error("Error login operation", e)) + // Caso que la task no se pueda crear en la BDD if (taskCreated.error != undefined) { console.error("[Sim.usecases]", taskCreated.error) diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index 22e1812..9698892 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -176,6 +176,7 @@ export class SimUsecases { console.log("[d] Cancelation ", cancelationWithId) await this.eventBus.publish([cancelationWithId]) const savedOrder = await this.saveOrder(cancelationWithId) + if (savedOrder.error != undefined) { console.error(savedOrder.error) return { @@ -205,11 +206,12 @@ export class SimUsecases { iccid: args.iccid } } + const pauseWithId = this.addMessage_id(pauseEvent) console.log("[d] Pause", pauseWithId) await this.eventBus.publish([pauseWithId]) - await this.saveOrder(pauseWithId) - const savedOrder = await this.saveOrder(pauseWithId) + //await this.saveOrder(pauseWithId) + const savedOrder = await this.saveOrder(pauseWithId) if (savedOrder.error != undefined) { console.error(savedOrder.error) diff --git a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts index abc6b7c..82532e2 100644 --- a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts +++ b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts @@ -35,4 +35,12 @@ simRoutes.post("/test", simController.test()) // Proceso especifico de ALAI para liberar sims canceladas simRoutes.post("/free", simController.free()) + +// Proxy + +const proxySimConnections = + + simRoutes.post("/simconnections/sim/activate",) +simRoutes.post("/simconnections/sim/pause") + export { simRoutes } diff --git a/packages/sim-entrada-eventos/package.json b/packages/sim-entrada-eventos/package.json index b59bdc9..ed10c87 100644 --- a/packages/sim-entrada-eventos/package.json +++ b/packages/sim-entrada-eventos/package.json @@ -53,6 +53,7 @@ "cors": "*", "dotenv": "*", "express": "*", + "http-proxy-middleware": "^3.0.5", "sim-shared": "sim-shared:*", "typescript": "*" }, diff --git a/packages/sim-shared/domain/operationsRepository.port.ts b/packages/sim-shared/domain/operationsRepository.port.ts index 237210e..6565004 100644 --- a/packages/sim-shared/domain/operationsRepository.port.ts +++ b/packages/sim-shared/domain/operationsRepository.port.ts @@ -12,7 +12,7 @@ export type ObjeniousOperation = { id?: number; /** Uuid del mensaje asociado a la operacion */ correlation_id?: string; - operation: "activate" | string; // TODO: completar y actualizar + operation: "activate" | "suspend" | "terminate" | string; // TODO: completar y actualizar retry_count?: number; max_retry?: number; max_date_retry?: string | null; diff --git a/yarn.lock b/yarn.lock index d670587..719cdff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -576,6 +576,15 @@ __metadata: languageName: node linkType: hard +"@types/http-proxy@npm:^1.17.15": + version: 1.17.17 + resolution: "@types/http-proxy@npm:1.17.17" + dependencies: + "@types/node": "npm:*" + checksum: 10/893e46e12be576baa471cf2fc13a4f0e413eaf30a5850de8fdbea3040e138ad4171234c59b986cf7137ff20a1582b254bf0c44cfd715d5ed772e1ab94dd75cd1 + languageName: node + linkType: hard + "@types/methods@npm:^1.1.4": version: 1.1.4 resolution: "@types/methods@npm:1.1.4" @@ -1154,7 +1163,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.4, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3": +"debug@npm:4, debug@npm:^4.1.1, debug@npm:^4.3.4, debug@npm:^4.3.6, debug@npm:^4.3.7, debug@npm:^4.4.0, debug@npm:^4.4.3": version: 4.4.3 resolution: "debug@npm:4.4.3" dependencies: @@ -1413,6 +1422,13 @@ __metadata: languageName: node linkType: hard +"eventemitter3@npm:^4.0.0": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 10/8030029382404942c01d0037079f1b1bc8fed524b5849c237b80549b01e2fc49709e1d0c557fa65ca4498fc9e24cff1475ef7b855121fcc15f9d61f93e282346 + languageName: node + linkType: hard + "expect-type@npm:^1.2.2": version: 1.3.0 resolution: "expect-type@npm:1.3.0" @@ -1527,6 +1543,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.0.0": + version: 1.16.0 + resolution: "follow-redirects@npm:1.16.0" + peerDependenciesMeta: + debug: + optional: true + checksum: 10/3fbe3d80b3b544c22705d837aa5d4a0d07a740d913534a2620b0a004c610af4148e3b58723536dd099aaa1c9d3a155964bde9665d6e5cb331460809a1fc572fd + languageName: node + linkType: hard + "follow-redirects@npm:^1.15.11": version: 1.15.11 resolution: "follow-redirects@npm:1.15.11" @@ -1788,6 +1814,31 @@ __metadata: languageName: node linkType: hard +"http-proxy-middleware@npm:^3.0.5": + version: 3.0.5 + resolution: "http-proxy-middleware@npm:3.0.5" + dependencies: + "@types/http-proxy": "npm:^1.17.15" + debug: "npm:^4.3.6" + http-proxy: "npm:^1.18.1" + is-glob: "npm:^4.0.3" + is-plain-object: "npm:^5.0.0" + micromatch: "npm:^4.0.8" + checksum: 10/83c1956be6451a5f4a2f3c7b3d84085dbd47e1efb5bb684c1ed668a6606c18c7c07be823b0dbba1326955b64cf88de2672492940b0b48d140215fbdb06105c9a + languageName: node + linkType: hard + +"http-proxy@npm:^1.18.1": + version: 1.18.1 + resolution: "http-proxy@npm:1.18.1" + dependencies: + eventemitter3: "npm:^4.0.0" + follow-redirects: "npm:^1.0.0" + requires-port: "npm:^1.0.0" + checksum: 10/2489e98aba70adbfd8b9d41ed1ff43528be4598c88616c558b109a09eaffe4bb35e551b6c75ac42ed7d948bb7530a22a2be6ef4f0cecacb5927be139f4274594 + languageName: node + linkType: hard + "https-proxy-agent@npm:^7.0.1": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" @@ -1865,7 +1916,7 @@ __metadata: languageName: node linkType: hard -"is-glob@npm:^4.0.1, is-glob@npm:~4.0.1": +"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" dependencies: @@ -1881,6 +1932,13 @@ __metadata: languageName: node linkType: hard +"is-plain-object@npm:^5.0.0": + version: 5.0.0 + resolution: "is-plain-object@npm:5.0.0" + checksum: 10/e32d27061eef62c0847d303125440a38660517e586f2f3db7c9d179ae5b6674ab0f469d519b2e25c147a1a3bc87156d0d5f4d8821e0ce4a9ee7fe1fcf11ce45c + languageName: node + linkType: hard + "is-promise@npm:^4.0.0": version: 4.0.0 resolution: "is-promise@npm:4.0.0" @@ -2842,6 +2900,7 @@ __metadata: cors: "npm:*" dotenv: "npm:*" express: "npm:*" + http-proxy-middleware: "npm:^3.0.5" prettier: "npm:*" sim-shared: "sim-shared:*" supertest: "npm:*" From 0bff55379fd70838108357189b0e6d612e6887e0 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 14 Apr 2026 16:15:01 +0200 Subject: [PATCH 26/41] tab --- packages/sim-entrada-eventos/aplication/Sim.usecases.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index 9698892..ad74047 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -174,6 +174,7 @@ export class SimUsecases { const cancelationWithId = this.addMessage_id(cancelationEvent) console.log("[d] Cancelation ", cancelationWithId) + await this.eventBus.publish([cancelationWithId]) const savedOrder = await this.saveOrder(cancelationWithId) From ef0f860b9dd71d7a058787c9570a6f2d846cacdf Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 14 Apr 2026 16:20:15 +0200 Subject: [PATCH 27/41] Erro handler --- .../sim-entrada-eventos/infrastructure/simRoutes.http.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts index 82532e2..5324b9c 100644 --- a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts +++ b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts @@ -38,9 +38,9 @@ simRoutes.post("/free", simController.free()) // Proxy -const proxySimConnections = +//const proxySimConnections = - simRoutes.post("/simconnections/sim/activate",) -simRoutes.post("/simconnections/sim/pause") +// simRoutes.post("/simconnections/sim/activate") +//simRoutes.post("/simconnections/sim/pause") export { simRoutes } From 1e98559f3a39ff2ceb8c4c962e418edbac26adb4 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 10:17:36 +0200 Subject: [PATCH 28/41] Fix --- .env | 3 ++ docs/sim-api/Activate.bru | 2 +- docs/sim-api/Cancel.bru | 2 +- .../aplication/Sim.router.ts | 2 +- .../aplication/Sim.controller.ts | 14 +++++++++ .../aplication/Sim.usecases.ts | 30 +++++++++++++++++++ packages/sim-entrada-eventos/index.ts | 2 ++ .../infrastructure/simRoutes.http.ts | 8 +---- .../infrastructure/simconnectionsRoutes.ts | 20 +++++++++++++ 9 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts diff --git a/.env b/.env index ecc3477..250da3e 100644 --- a/.env +++ b/.env @@ -27,3 +27,6 @@ PGHOST=localhost PGUSER=alvar PGPASSWORD=alvar PGPORT=5433 + +# Proxy +SIMCONNECTIONSURL=http://sim-connections.savefamilygps.net diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index 6417025..f22b24c 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125068890694 + iccid: 8933201125068890408 offer: SAVEFAMILY1 } diff --git a/docs/sim-api/Cancel.bru b/docs/sim-api/Cancel.bru index 778e066..642091a 100644 --- a/docs/sim-api/Cancel.bru +++ b/docs/sim-api/Cancel.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125068887864 + iccid: 8933201125068890074 } settings { diff --git a/packages/sim-consumidor-objenious/aplication/Sim.router.ts b/packages/sim-consumidor-objenious/aplication/Sim.router.ts index 79a001e..18380b6 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.router.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.router.ts @@ -19,7 +19,7 @@ export class SimRouter { ["activate", this.simController.activate()], ["pause", this.simController.suspend()], ["cancel", this.simController.terminate()], - ["reActivate", this.simController.reActivate()], + ["reactivation", this.simController.reActivate()], ["preActivate", this.simController.preActivate()] ]); } diff --git a/packages/sim-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index 9afe780..79bef42 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -131,6 +131,20 @@ export class SimController { }) } + + public reActivation() { + 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.reActivation(args), + onError: (d, e) => console.error("[x] Error activacion: ", d, e), + onSuccess: console.log + }) + } public cancelation() { return this.controllerGenerator<{ iccid: string }, { iccid: string, compañia: string }>({ validator: iccidValidator, diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index ad74047..95cf670 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -130,6 +130,36 @@ export class SimUsecases { } } + async reActivation(args: { iccid: string, compañia: string, offer: string }): + Promise> { + const activationEvent = { + key: `sim.${args.compañia}.reactivation`, + payload: { + iccid: args.iccid, + offer: args.offer + } + } + const activationWithId = this.addMessage_id(activationEvent) + console.log("[d] Reactivation ", activationWithId) + await this.eventBus.publish([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: "reactivation", + message_id: createdOrder.data?.correlation_id + } + } + } + async preActivation(args: { iccid: string, compañia: string }): Promise> { diff --git a/packages/sim-entrada-eventos/index.ts b/packages/sim-entrada-eventos/index.ts index 39ea518..cd3953b 100644 --- a/packages/sim-entrada-eventos/index.ts +++ b/packages/sim-entrada-eventos/index.ts @@ -5,6 +5,7 @@ import { simRoutes } from "./infrastructure/simRoutes.http.js" import { rabbitmqEventBus } from '#config/eventBusConfig.js'; import { env } from "#config/env/index.js" import { orderRoutes } from "#adapters/orderRoutes.http.js"; +import { connectionsRoutes } from "#adapters/simconnectionsRoutes.js"; const PORT = env.API_PORT const HOSTNAME = "0.0.0.0" @@ -26,6 +27,7 @@ app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use("/sim", simRoutes) +app.use("/simconnections", connectionsRoutes) app.use("/orders", orderRoutes) app.use("/docs", express.static(path.join(process.cwd(), '../../docs'))) diff --git a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts index 5324b9c..9276ea6 100644 --- a/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts +++ b/packages/sim-entrada-eventos/infrastructure/simRoutes.http.ts @@ -23,6 +23,7 @@ simRoutes.get("/status", () => { }) simRoutes.post("/save", simController.save()) simRoutes.post("/activate", simController.activation()) +simRoutes.post("/reActivate", simController.reActivation()) simRoutes.post("/preActivate", simController.preactivation()) @@ -36,11 +37,4 @@ simRoutes.post("/test", simController.test()) simRoutes.post("/free", simController.free()) -// Proxy - -//const proxySimConnections = - -// simRoutes.post("/simconnections/sim/activate") -//simRoutes.post("/simconnections/sim/pause") - export { simRoutes } diff --git a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts new file mode 100644 index 0000000..47d2e01 --- /dev/null +++ b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts @@ -0,0 +1,20 @@ +import { Router } from "express" +import { createProxyMiddleware } from "http-proxy-middleware" + +export const connectionsRoutes = Router() + +const CONNECTIONSURL = "" // TODO: Meter al ENV + +connectionsRoutes.post("/sim/activate", createProxyMiddleware({ + target: CONNECTIONSURL + "/sim/activate", + changeOrigin: true, + // pathrewrite + //on proxy req +})) + +connectionsRoutes.post("/sim/pause", createProxyMiddleware({ + target: CONNECTIONSURL + "/sim/activate", + changeOrigin: true +})) + + From 1f78f4a3e1c10a4dbd6ec9bda9ad9eb1a8c49136 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 10:18:49 +0200 Subject: [PATCH 29/41] Periodo pruebas reducidio --- packages/sim-objenious-cron/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index e325b98..7829811 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -62,6 +62,7 @@ async function startCron() { orderRepository ) + await objTask.getPendingOperations() const PERIODO_PETICIONES = 10 * 60 * 1000 const interval = setInterval(async () => { try { From a35a6c2b6097e2a1f35e7896b3f5e2900cd3b72d Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 10:27:58 +0200 Subject: [PATCH 30/41] Error endpoint --- docs/sim-api/Activate.bru | 2 +- docs/sim-api/ReActivate.bru | 20 +++++++++++++++++++ .../aplication/Sim.controller.ts | 2 +- .../aplication/Sim.usecases.ts | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 docs/sim-api/ReActivate.bru diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index f22b24c..4382fe1 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125068890408 + iccid: 8933201125065160414 offer: SAVEFAMILY1 } diff --git a/docs/sim-api/ReActivate.bru b/docs/sim-api/ReActivate.bru new file mode 100644 index 0000000..ad18472 --- /dev/null +++ b/docs/sim-api/ReActivate.bru @@ -0,0 +1,20 @@ +meta { + name: ReActivate + type: http + seq: 13 +} + +post { + url: {{baseurl}}/sim/reActivate + body: formUrlEncoded + auth: inherit +} + +body:form-urlencoded { + iccid: 8933201125065160380 +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/packages/sim-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index 79bef42..2324495 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -141,7 +141,7 @@ export class SimController { return { iccid, compañia, offer } }, useCase: (args) => this.simUseCases.reActivation(args), - onError: (d, e) => console.error("[x] Error activacion: ", d, e), + onError: (d, e) => console.error("[x] Error reactivacion: ", d, e), onSuccess: console.log }) } diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index 95cf670..de570c6 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -131,7 +131,7 @@ export class SimUsecases { } async reActivation(args: { iccid: string, compañia: string, offer: string }): - Promise> { + Promise> { const activationEvent = { key: `sim.${args.compañia}.reactivation`, payload: { @@ -154,7 +154,7 @@ export class SimUsecases { return { data: { iccid: args.iccid, - operation: "reactivation", + operation: "reactivate", message_id: createdOrder.data?.correlation_id } } From 1dc4eb5648665870ee6262a4d6a3ebcd50f3a551 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 10:28:52 +0200 Subject: [PATCH 31/41] validador --- packages/sim-entrada-eventos/aplication/Sim.controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/sim-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index 2324495..1508f3b 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -134,7 +134,7 @@ export class SimController { public reActivation() { return this.controllerGenerator<{ iccid: string, offer: string }, { iccid: string, offer: string, compañia: string }>({ - validator: activationValidator, + validator: iccidValidator, mapBody: (b) => { const { iccid, offer } = b const compañia = companyFromIccid(iccid) From eac74ef0cd7c1fb2cf0145abfe49b334ea158678 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 10:31:21 +0200 Subject: [PATCH 32/41] Error key evento --- packages/sim-consumidor-objenious/aplication/Sim.router.ts | 2 +- packages/sim-entrada-eventos/aplication/Sim.controller.ts | 1 + packages/sim-entrada-eventos/aplication/Sim.usecases.ts | 2 +- packages/sim-shared/domain/SimEvents.ts | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.router.ts b/packages/sim-consumidor-objenious/aplication/Sim.router.ts index 18380b6..7a34e29 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.router.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.router.ts @@ -19,7 +19,7 @@ export class SimRouter { ["activate", this.simController.activate()], ["pause", this.simController.suspend()], ["cancel", this.simController.terminate()], - ["reactivation", this.simController.reActivate()], + ["reactivate", this.simController.reActivate()], ["preActivate", this.simController.preActivate()] ]); } diff --git a/packages/sim-entrada-eventos/aplication/Sim.controller.ts b/packages/sim-entrada-eventos/aplication/Sim.controller.ts index 1508f3b..62ebfc4 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.controller.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.controller.ts @@ -145,6 +145,7 @@ export class SimController { onSuccess: console.log }) } + public cancelation() { return this.controllerGenerator<{ iccid: string }, { iccid: string, compañia: string }>({ validator: iccidValidator, diff --git a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts index de570c6..a81fb3d 100644 --- a/packages/sim-entrada-eventos/aplication/Sim.usecases.ts +++ b/packages/sim-entrada-eventos/aplication/Sim.usecases.ts @@ -133,7 +133,7 @@ export class SimUsecases { async reActivation(args: { iccid: string, compañia: string, offer: string }): Promise> { const activationEvent = { - key: `sim.${args.compañia}.reactivation`, + key: `sim.${args.compañia}.reactivate`, payload: { iccid: args.iccid, offer: args.offer diff --git a/packages/sim-shared/domain/SimEvents.ts b/packages/sim-shared/domain/SimEvents.ts index ff1ec29..84649db 100644 --- a/packages/sim-shared/domain/SimEvents.ts +++ b/packages/sim-shared/domain/SimEvents.ts @@ -24,7 +24,7 @@ export namespace SimEvents { } export type reActivation = DomainEvent & { - key: `sim.${string}.reActivate`, + key: `sim.${string}.reactivate`, payload: { iccid: string }, From bb4bce4a6d345181ca57dd6a69e9e7a0f046fa8d Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 11:11:55 +0200 Subject: [PATCH 33/41] fix --- docs/sim-api/ReActivate.bru | 1 + docs/sim-objenious/Mass action list.bru | 4 ++-- .../aplication/Sim.usecases.ts | 19 ++++++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/sim-api/ReActivate.bru b/docs/sim-api/ReActivate.bru index ad18472..50308ab 100644 --- a/docs/sim-api/ReActivate.bru +++ b/docs/sim-api/ReActivate.bru @@ -12,6 +12,7 @@ post { body:form-urlencoded { iccid: 8933201125065160380 + ~offer: SAVEFAMILY1 } settings { diff --git a/docs/sim-objenious/Mass action list.bru b/docs/sim-objenious/Mass action list.bru index b83a34a..ebbec4b 100644 --- a/docs/sim-objenious/Mass action list.bru +++ b/docs/sim-objenious/Mass action list.bru @@ -5,13 +5,13 @@ meta { } get { - url: {{actionsUrl}}/massActions?massActionId=5192767 + url: {{actionsUrl}}/massActions?massActionId=5363116 body: formUrlEncoded auth: bearer } params:query { - massActionId: 5192767 + massActionId: 5363116 ~identifier.identifierType: ICCID ~identifier.identifiers: 8933201125065160463,8933201125065160422 } diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 304d13f..f17086a 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -199,16 +199,29 @@ export class SimUseCases { } } - public reActivate(pauseData: ActionData): () => Promise> { + public reActivate(reactivateData: ActionData): () => Promise> { const OPERATION_URL = "/actions/reactivateLine" return async () => { const req = this.httpClient.client.post(OPERATION_URL, { - ...pauseData + ...reactivateData }) try { const response = await req + // Creacion de la operacion inicial, antes de tener los datos + const operation: ObjeniousOperation = { + operation: "reactivate", + iccids: reactivateData.identifier.identifiers[0], + status: "noMassID", + request_id: response.data.requestId, + correlation_id: reactivateData.correlation_id + } + + // TODO: Esto tiene poco sentido si la operacion ya se + // tenia que haber creado en el generador + this.logOperation(operation) + .then().catch(e => console.error("Error login operation", e)) if (response.status == 200) { console.log("[o] Sim solicitud de reactivacion ", response.data) return >{ @@ -224,7 +237,7 @@ export class SimUseCases { } catch (error) { console.error("[x] Error reactivacion", (error as AxiosError).response?.status) return >{ - error: "Error reactivando la sim" + pauseData.identifier, + error: "Error reactivando la sim" + reactivateData.identifier, data: undefined } } From e359acc1d587abb4d487323442bd8208da40f801 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 11:47:33 +0200 Subject: [PATCH 34/41] Validaciones pausa instantaneas --- packages/sim-objenious-cron/index.ts | 1 - .../ObjeniousOperationRepository.test.ts | 2 +- .../infrastructure/ObjeniousOperationRepository.ts | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index 7829811..b624356 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -81,7 +81,6 @@ async function startCron() { } }, PERIODO_VOLCADO) - await pauseTask.run() const PERIODO_CANCELACIONES = 60 * 60 * 1000; const clacelacionesInterval = setInterval(async () => { diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts index 18458d0..8ab56d3 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts @@ -8,7 +8,7 @@ describe("[Integration] Test API requests", () => { postgresClient ) - it("Read /lines with multiple iccids", () => { + it("Read last sucessfull operation by line", () => { }) }) diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index 5010326..6dc51fd 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -146,6 +146,20 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { } } + + async getLastOperationOfLine(iccid: string) { + const query = ` + SELECT * FROM public.objenious_operation + WHERE iccids = $1 + ORDER BY id asc limit 1 + ` + const values = [iccid]; + const { rows } = await this.pgClient.query(query, values); + return >{ + data: rows[0] + } + } + async updateOperation(data: ObjeniousOperationChange): Promise> { const client = await this.pgClient.connect(); const { From 33d260310c70fc9dd34e720918d928af8167eeec Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 12:47:26 +0200 Subject: [PATCH 35/41] =?UTF-8?q?Reactivate=20cuando=20la=20linea=20est?= =?UTF-8?q?=C3=A9=20suspendida?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aplication/Sim.usecases.ts | 10 +++++++ .../ObjeniousOperationRepository.test.ts | 29 +++++++++++++++++-- .../ObjeniousOperationRepository.ts | 2 +- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index f17086a..28bbe61 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -116,6 +116,16 @@ export class SimUseCases { public activate(activationData: ActivationData): () => Promise> { const OPERATION_URL = "/actions/activateLine" return async () => { + const iccid = activationData.identifier.identifiers + // Comporbación excepcional para saber si la linea está suspendida + console.log("statusLinea, ", iccid) + const statusLinea = await this.objeniousRepository.getLinesAPI("ICCID", [String(iccid)]) + + if (statusLinea.data != undefined && statusLinea.data[0].status.billingStatus == "SUSPENDED") { + const res = await this.reActivate(activationData)() + return res; + } + const req = this.httpClient.client.post(OPERATION_URL, { dueDate: activationData.dueDate, identifier: activationData.identifier, diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts index 8ab56d3..8304b64 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts @@ -1,6 +1,20 @@ -import { describe, it } from "node:test"; +import { before, describe, it } from "node:test"; import { ObjeniousOperationsRepository } from "./ObjeniousOperationRepository.js"; import { httpObjClient, postgresClient } from "../config/config.test.js"; +import { ObjeniousOperation } from "../domain/operationsRepository.port.js"; + +const correctOperation: ObjeniousOperation = { + iccids: "test", + operation: "activate", + status: "finished" +} + +const errorOperation: ObjeniousOperation = { + iccids: "test", + operation: "terminate", + status: "error", + error: "mensaje de error" +} describe("[Integration] Test API requests", () => { const repository = new ObjeniousOperationsRepository( @@ -8,7 +22,18 @@ describe("[Integration] Test API requests", () => { postgresClient ) - it("Read last sucessfull operation by line", () => { + before(async () => { + await repository.createOperation(correctOperation) + await repository.createOperation(errorOperation) + }) + it("Read last operation by line", () => { + /** + * Objetivo: + * - Cuando se va a hacer una operacion de sim hay que cancelarla directamente si: + * - Ya hay una en curso del mismo tipo. + * - Ya ha terminado una del mismo tipo. + * - Se ignoran las errorneas + */ }) }) diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index 6dc51fd..4346ff7 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -150,7 +150,7 @@ export class ObjeniousOperationsRepository implements IOperationsRepository { async getLastOperationOfLine(iccid: string) { const query = ` SELECT * FROM public.objenious_operation - WHERE iccids = $1 + WHERE iccids = $1 and error is null ORDER BY id asc limit 1 ` const values = [iccid]; From cffee785b255451a110764cc22c05d40bf8431df Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 12:48:08 +0200 Subject: [PATCH 36/41] logs --- packages/sim-consumidor-objenious/aplication/Sim.usecases.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 28bbe61..6bb3260 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -118,9 +118,8 @@ export class SimUseCases { return async () => { const iccid = activationData.identifier.identifiers // Comporbación excepcional para saber si la linea está suspendida - console.log("statusLinea, ", iccid) const statusLinea = await this.objeniousRepository.getLinesAPI("ICCID", [String(iccid)]) - + console.log("statusLinea, ", iccid, statusLinea) if (statusLinea.data != undefined && statusLinea.data[0].status.billingStatus == "SUSPENDED") { const res = await this.reActivate(activationData)() return res; From 7001fccbf792e8869164e561f7f9c0fa5b6fd297 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 13:50:20 +0200 Subject: [PATCH 37/41] Problema de creacion de operacion de pausa/cancelacion --- docs/sim-api/Activate.bru | 2 +- .../sim-consumidor-objenious/aplication/Sim.usecases.ts | 9 +++++---- packages/sim-shared/domain/operationsRepository.port.ts | 3 +-- .../infrastructure/ObjeniousOperationRepository.test.ts | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index 4382fe1..55487be 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125065160414 + iccid: 8933201125065160380 offer: SAVEFAMILY1 } diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 6bb3260..8856b97 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -306,9 +306,9 @@ export class SimUseCases { status: "running", correlation_id: correlation_id } - - this.logOperation(operation) - .then().catch(e => console.error("Error login operation", e)) + // No se registra hasta que no pase por la tabla de pausas + // this.logOperation(operation) + // .then().catch(e => console.error("Error login operation", e)) const fail = (error: string) => { console.error("[Sim.usecases]", error) @@ -394,9 +394,10 @@ export class SimUseCases { correlation_id: correlation_id } + /** this.logOperation(operation) .then().catch(e => console.error("Error login operation", e)) - + */ // Caso que la task no se pueda crear en la BDD if (taskCreated.error != undefined) { console.error("[Sim.usecases]", taskCreated.error) diff --git a/packages/sim-shared/domain/operationsRepository.port.ts b/packages/sim-shared/domain/operationsRepository.port.ts index 6565004..898fd10 100644 --- a/packages/sim-shared/domain/operationsRepository.port.ts +++ b/packages/sim-shared/domain/operationsRepository.port.ts @@ -27,8 +27,7 @@ export type ObjeniousOperation = { } export type ObjeniousOperationChange = { - id?: number; - operation_id: number; + id?: number; operation_id: number; info?: string | null; error?: string | null; new_status: StatusEnum; diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts index 8304b64..aa80a27 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.test.ts @@ -33,7 +33,7 @@ describe("[Integration] Test API requests", () => { * - Cuando se va a hacer una operacion de sim hay que cancelarla directamente si: * - Ya hay una en curso del mismo tipo. * - Ya ha terminado una del mismo tipo. - * - Se ignoran las errorneas + * - Se ignoran las erroneas */ }) }) From 15b70309daf5c4b838f32c6ecf761023f05915af Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Wed, 15 Apr 2026 15:11:13 +0200 Subject: [PATCH 38/41] Fix networkStatus --- .../aplication/Sim.usecases.ts | 2 +- .../infrastructure/simconnectionsRoutes.ts | 39 ++++++++++++++++++- .../tasks/check_pause_terminate.ts | 6 ++- packages/sim-shared/domain/Order.ts | 3 +- .../infrastructure/OrderRepository.ts | 4 +- 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 8856b97..8fc34ee 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -120,7 +120,7 @@ export class SimUseCases { // Comporbación excepcional para saber si la linea está suspendida const statusLinea = await this.objeniousRepository.getLinesAPI("ICCID", [String(iccid)]) console.log("statusLinea, ", iccid, statusLinea) - if (statusLinea.data != undefined && statusLinea.data[0].status.billingStatus == "SUSPENDED") { + if (statusLinea.data != undefined && statusLinea.data[0].status.networkStatus == "SUSPENDED") { const res = await this.reActivate(activationData)() return res; } diff --git a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts index 47d2e01..f955865 100644 --- a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts +++ b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts @@ -5,7 +5,7 @@ export const connectionsRoutes = Router() const CONNECTIONSURL = "" // TODO: Meter al ENV -connectionsRoutes.post("/sim/activate", createProxyMiddleware({ +connectionsRoutes.post("/", createProxyMiddleware({ target: CONNECTIONSURL + "/sim/activate", changeOrigin: true, // pathrewrite @@ -13,8 +13,43 @@ connectionsRoutes.post("/sim/activate", createProxyMiddleware({ })) connectionsRoutes.post("/sim/pause", createProxyMiddleware({ - target: CONNECTIONSURL + "/sim/activate", + target: CONNECTIONSURL + "/sim/pause", changeOrigin: true })) +// Rutas +/** +connectionsRoutes.post('/simconnections/alai/preactivate',); +connectionsRoutes.get('/simconnections/alai/pause',); +connectionsRoutes.post('/simconnections/alai/terminate',); +connectionsRoutes.get('/simconnections/alai/pauseByPhone',); +connectionsRoutes.get('/simconnections/alai/active',); +connectionsRoutes.get('/simconnections/alai/change_orderid',); +connectionsRoutes.get('/simconnections/alai/select',); +connectionsRoutes.get('/simconnections/alai/select-iccid',); +connectionsRoutes.get('/simconnections/alai/selectFromDb',); +connectionsRoutes.get('/simconnections/alai/selectPage',); +connectionsRoutes.post('/simconnections/alai/schedulePause',); +connectionsRoutes.get('/simconnections/shopify/getbyWP',); +connectionsRoutes.get('/simconnections/shopify/getbyWPS',); + +/// + +connectionsRoutes.get('/simconnections/sim/associate',); +connectionsRoutes.post('/simconnections/sim/search',); +connectionsRoutes.post('/simconnections/sim/historic',); +connectionsRoutes.post('/simconnections/sim/update',); + +/// + +connectionsRoutes.post('/simconnections/nos/activate',); +connectionsRoutes.get('/simconnections/nos/select',); +connectionsRoutes.get('/simconnections/nos/selectPage',); + +//Unificación +connectionsRoutes.post('/simconnections/sim/active',); // True false +connectionsRoutes.patch('/simconnections/sim/pause',); +connectionsRoutes.get('/simconnections/sim/select',); +connectionsRoutes.get('/simconnections/sim/select-phone',); +**/ diff --git a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts index a43a832..230c45d 100644 --- a/packages/sim-objenious-cron/tasks/check_pause_terminate.ts +++ b/packages/sim-objenious-cron/tasks/check_pause_terminate.ts @@ -118,9 +118,11 @@ export class PauseTerminateTask { switch (linea.status.billingStatus) { case "ACTIVATED": - let exito = false; let result = null; - // IMPORTANTE COMRPOBAR EL DUE DATE + + // Se termina el proceso aqui pero pasa a ser una operación de + // objenious por lo que puede fallar y quedaria registrado en + // la tabla objenious_operation switch (operacionTipo) { case "suspend": result = await this.simUsecases.suspend(actionData)() diff --git a/packages/sim-shared/domain/Order.ts b/packages/sim-shared/domain/Order.ts index 7f3d35b..df1c2f3 100644 --- a/packages/sim-shared/domain/Order.ts +++ b/packages/sim-shared/domain/Order.ts @@ -80,7 +80,8 @@ export type FinishOrderDTO = IdOrCorrelationID & { - reason?: string + reason?: string, + end_date?: Date } export type ErrorOrderDTO = diff --git a/packages/sim-shared/infrastructure/OrderRepository.ts b/packages/sim-shared/infrastructure/OrderRepository.ts index 757e9e8..bab9436 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.ts @@ -302,8 +302,8 @@ export class OrderRepository { UPDATE order_tracking SET status = 'finished', - update_date = (now() at time zone 'utc'), - finish_date = (now() at time zone 'utc') + update_date = now(), + finish_date = now() WHERE id = $1 RETURNING id, status, update_date; ` From 0aa52feaac717f86d41d96223a1f4ba429bd4b70 Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 16 Apr 2026 11:51:30 +0200 Subject: [PATCH 39/41] Proxy --- .env | 2 +- docs/sim-api/environments/simconnections.bru | 4 ++ docs/sim-api/test proxy.bru | 20 +++++++ .../sim-entrada-eventos/config/env/index.ts | 1 + .../infrastructure/simconnectionsRoutes.ts | 55 ++++++++++++++++--- 5 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 docs/sim-api/environments/simconnections.bru create mode 100644 docs/sim-api/test proxy.bru diff --git a/.env b/.env index 250da3e..d72f207 100644 --- a/.env +++ b/.env @@ -29,4 +29,4 @@ PGPASSWORD=alvar PGPORT=5433 # Proxy -SIMCONNECTIONSURL=http://sim-connections.savefamilygps.net +CONNECTIONS_URL=https://sim-connections.savefamilygps.net diff --git a/docs/sim-api/environments/simconnections.bru b/docs/sim-api/environments/simconnections.bru new file mode 100644 index 0000000..d7e876f --- /dev/null +++ b/docs/sim-api/environments/simconnections.bru @@ -0,0 +1,4 @@ +vars { + baseurl: http://sim-connections.savefamilygps.net +} +color: #C77A0F diff --git a/docs/sim-api/test proxy.bru b/docs/sim-api/test proxy.bru new file mode 100644 index 0000000..d4e17b0 --- /dev/null +++ b/docs/sim-api/test proxy.bru @@ -0,0 +1,20 @@ +meta { + name: test proxy + type: http + seq: 14 +} + +get { + url: {{baseurl}}/simconnections/alai/select?iccid=1111111111111111111 + body: none + auth: inherit +} + +params:query { + iccid: 1111111111111111111 +} + +settings { + encodeUrl: true + timeout: 0 +} diff --git a/packages/sim-entrada-eventos/config/env/index.ts b/packages/sim-entrada-eventos/config/env/index.ts index 3923ba0..c8d6370 100644 --- a/packages/sim-entrada-eventos/config/env/index.ts +++ b/packages/sim-entrada-eventos/config/env/index.ts @@ -22,4 +22,5 @@ export const env = { RABBITMQ_SECURE: process.env.RABBITMQ_SECURE, RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL, RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST), + CONNECTIONS_URL: String(process.env.CONNECTIONS_URL) }; diff --git a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts index f955865..e0173bb 100644 --- a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts +++ b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts @@ -1,21 +1,58 @@ +import { env } from "#config/env/index.js" import { Router } from "express" +import { ClientRequest, IncomingMessage } from "http" import { createProxyMiddleware } from "http-proxy-middleware" +import { Request } from "express" export const connectionsRoutes = Router() -const CONNECTIONSURL = "" // TODO: Meter al ENV +const CONNECTIONS_URL = env.CONNECTIONS_URL// TODO: Meter al ENV +//const CONNECTIONS_URL = "http://sf-nfc-server.savefamilygps.net" -connectionsRoutes.post("/", createProxyMiddleware({ - target: CONNECTIONSURL + "/sim/activate", +console.log("CONNURL: ", CONNECTIONS_URL) + +connectionsRoutes.use("", createProxyMiddleware({ + target: CONNECTIONS_URL, changeOrigin: true, - // pathrewrite - //on proxy req + pathRewrite: { + '^/': "/simconnections/" + }, + on: { + proxyReq: (proxyReq: ClientRequest, req: Request) => { + // 1. Construimos la URL de origen (lo que recibe tu proxy) + console.log(JSON.stringify(req)) + const protocol = req.protocol; + const host = req.get('host'); + const originalFullUrl = `${protocol}://${host}${req.originalUrl}`; + + // 2. Construimos la URL de destino (hacia donde se envía) + // proxyReq.path ya incluye el path rewrite y los query params + const destinationFullUrl = `${CONNECTIONS_URL}${proxyReq.path}`; + + console.log('──────────────────────────────────────────────────'); + console.log(`[PROXY_DEBUG]`); + console.log(` ENTRADA: ${originalFullUrl}`); + console.log(` MÉTODO : ${req.method}`); + console.log(` DESTINO: ${destinationFullUrl}`); + console.log('──────────────────────────────────────────────────'); + + //console.log(`[Proxy Req]: ${req.method} ${req.url} -> ${proxyReq.path}`); + }, + proxyRes: (proxyRes, req, res) => { + console.log(`[Proxy Res] Status: ${proxyRes.statusCode} desde ${req.url}`); + }, + error: (err, req, res) => { + console.error('[Proxy Error]:', err); + + // Validamos que 'res' tenga el método 'status' (típico de Express Response) + if ('status' in res) { + //@ts-ignore + res.status(500).json({ message: 'Error interno en el Gateway' }); + } + }, + } })) -connectionsRoutes.post("/sim/pause", createProxyMiddleware({ - target: CONNECTIONSURL + "/sim/pause", - changeOrigin: true -})) // Rutas From 602878acf4fcfefd3c2108027cac806cd71bae9e Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 16 Apr 2026 12:01:31 +0200 Subject: [PATCH 40/41] console log de debug --- .../infrastructure/simconnectionsRoutes.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts index e0173bb..3f04022 100644 --- a/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts +++ b/packages/sim-entrada-eventos/infrastructure/simconnectionsRoutes.ts @@ -19,24 +19,19 @@ connectionsRoutes.use("", createProxyMiddleware({ }, on: { proxyReq: (proxyReq: ClientRequest, req: Request) => { - // 1. Construimos la URL de origen (lo que recibe tu proxy) - console.log(JSON.stringify(req)) const protocol = req.protocol; const host = req.get('host'); const originalFullUrl = `${protocol}://${host}${req.originalUrl}`; - - // 2. Construimos la URL de destino (hacia donde se envía) - // proxyReq.path ya incluye el path rewrite y los query params const destinationFullUrl = `${CONNECTIONS_URL}${proxyReq.path}`; - - console.log('──────────────────────────────────────────────────'); + /* + constnsole.log('──────────────────────────────────────────────────'); console.log(`[PROXY_DEBUG]`); console.log(` ENTRADA: ${originalFullUrl}`); console.log(` MÉTODO : ${req.method}`); console.log(` DESTINO: ${destinationFullUrl}`); console.log('──────────────────────────────────────────────────'); - - //console.log(`[Proxy Req]: ${req.method} ${req.url} -> ${proxyReq.path}`); + */ + console.log(`[Proxy Req]: ${req.method} ${req.url} -> ${proxyReq.path}`); }, proxyRes: (proxyRes, req, res) => { console.log(`[Proxy Res] Status: ${proxyRes.statusCode} desde ${req.url}`); From 964ea6add91ac753cbc669f2b7bb334c3df4730e Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Thu, 16 Apr 2026 12:44:31 +0200 Subject: [PATCH 41/41] Inicio migracion NOS --- packages/sim-consumidor-nos/.env | 4 +- .../sim-consumidor-nos/config/env/index.ts | 16 ++++- .../config/eventBus.config.ts | 69 +++++++++++++++++++ .../config/eventBusConfig.ts | 39 ----------- packages/sim-consumidor-nos/index.ts | 2 +- packages/sim-consumidor-nos/package.json | 3 +- .../config/eventBus.config.ts | 8 +-- 7 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 packages/sim-consumidor-nos/config/eventBus.config.ts delete mode 100644 packages/sim-consumidor-nos/config/eventBusConfig.ts diff --git a/packages/sim-consumidor-nos/.env b/packages/sim-consumidor-nos/.env index 6979d6b..597b9fa 100644 --- a/packages/sim-consumidor-nos/.env +++ b/packages/sim-consumidor-nos/.env @@ -1,5 +1,3 @@ -PORT=3000 -RABBITMQ_USER=guest -RABBITMQ_PASSWORD=guest +NOS_BASE_URL=localhost ENVIORMENT=development diff --git a/packages/sim-consumidor-nos/config/env/index.ts b/packages/sim-consumidor-nos/config/env/index.ts index 021873e..4aecfbb 100644 --- a/packages/sim-consumidor-nos/config/env/index.ts +++ b/packages/sim-consumidor-nos/config/env/index.ts @@ -1,5 +1,16 @@ import { loadEnvFile } from "node:process"; -loadEnvFile("../../.env") +import path from "node:path"; + +try { + loadEnvFile(path.join("./.env")) // base +} catch (e) { + console.error("Error cargando el .env desde ./.env") +} +try { + loadEnvFile(path.join("../../.env")) // Global +} catch (e) { + console.error("Error cargando el .env desde ../../.env") +} export const env = { ENVIRONMENT: process.env.ENVIORMENT, @@ -18,5 +29,8 @@ export const env = { RABBITMQ_SECURE: process.env.RABBITMQ_SECURE, RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL, RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST), + + // ESPECIFICO NOS + NOS_BASE_URL: String(process.env.NOS_BASE_URL) }; diff --git a/packages/sim-consumidor-nos/config/eventBus.config.ts b/packages/sim-consumidor-nos/config/eventBus.config.ts new file mode 100644 index 0000000..5c84601 --- /dev/null +++ b/packages/sim-consumidor-nos/config/eventBus.config.ts @@ -0,0 +1,69 @@ +import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js" +import { Channel } from "amqp-connection-manager" +import { env } from "./env/index.js" + +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, + buildStructure: buildQueues, + maxRetry: 5 +}) + +async function buildQueues(channel: Channel) { + const QUEUES = { + NOS: "sim.nos", + NOSDLX: "sim.nos.dlx", + NOSDEL: "sim.nos.delayed", + } + + const EXCHANGES = { + MAIN: "sim.exchange", + DLX: "sim.ex.nos.dlx", + DEL: "sim.ex.nos.delayed" + } + + const DELAY = 10 * 1000 + const BASE_NOS_KEY = "sim.nos.#" + + await channel.assertExchange(EXCHANGES.DEL, "topic") + await channel.assertExchange(EXCHANGES.DLX, "topic") + await channel.assertExchange(EXCHANGES.MAIN, "topic") + + await channel.assertQueue(QUEUES.NOS) + await channel.assertQueue(QUEUES.NOSDLX) + await channel.assertQueue(QUEUES.NOSDEL, { + durable: true, + arguments: { + 'x-message-ttl': DELAY, + 'x-dead-letter-exchange': EXCHANGES.MAIN, + } + }) + + // Cola dead-letter + await channel.bindQueue(QUEUES.NOSDLX, EXCHANGES.DLX, "sim.nos.#") + // Cola delay + await channel.bindQueue(QUEUES.NOSDEL, EXCHANGES.DEL, BASE_NOS_KEY) + // Cola nos -> main exchange + await channel.bindQueue(QUEUES.NOS, EXCHANGES.MAIN, BASE_NOS_KEY) + +} + +export async function startRMQClient() { + await rabbitmqEventBus.connect() + return rabbitmqEventBus +} diff --git a/packages/sim-consumidor-nos/config/eventBusConfig.ts b/packages/sim-consumidor-nos/config/eventBusConfig.ts deleted file mode 100644 index 9a0e1f3..0000000 --- a/packages/sim-consumidor-nos/config/eventBusConfig.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js" -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().catch(async e => { - console.error("Error en la conexion RMQ") - await rabbitmqEventBus.connect() - }) - - // Bindings especificos, deberia meterlos en la clase - try { - await rabbitmqEventBus.channel?.assertQueue("sim.nos") - } catch { - console.log("[i] Cola de sims de nos creada") - await 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 index 36cce2a..3214b9d 100644 --- a/packages/sim-consumidor-nos/index.ts +++ b/packages/sim-consumidor-nos/index.ts @@ -1,5 +1,5 @@ -import { startRMQClient } from "#config/eventBusConfig" +import { startRMQClient } from "#config/eventBus.config.js" import { SimNosController } from "./aplication/SimNOS.controller.js" async function startWorker() { diff --git a/packages/sim-consumidor-nos/package.json b/packages/sim-consumidor-nos/package.json index ee4549f..40057f5 100644 --- a/packages/sim-consumidor-nos/package.json +++ b/packages/sim-consumidor-nos/package.json @@ -7,7 +7,8 @@ "test": "echo \"Error: no test specified\" && exit 1", "build": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp package.json ../../dist/packages/sim-consumidor-nos/", "esbuild": "esbuild index.ts --platform=node", - "start": "node ../../dist/packages/sim-consumidor-nos/index.js" + "start": "node ../../dist/packages/sim-consumidor-nos/index.js", + "dev": "tsx watch index.ts" }, "author": "", "license": "ISC", diff --git a/packages/sim-consumidor-objenious/config/eventBus.config.ts b/packages/sim-consumidor-objenious/config/eventBus.config.ts index 4c5db47..81f583d 100644 --- a/packages/sim-consumidor-objenious/config/eventBus.config.ts +++ b/packages/sim-consumidor-objenious/config/eventBus.config.ts @@ -27,8 +27,8 @@ export const rabbitmqEventBus = new RabbitMQEventBus({ async function buildQueues(channel: Channel) { const QUEUES = { OBJ: "sim.objenious", - DLX: "sim.objenious.dlx", - DEL: "sim.objenious.delayed" + OBJDLX: "sim.objenious.dlx", + OBJDEL: "sim.objenious.delayed", } const EXCHANGES = { @@ -45,8 +45,8 @@ async function buildQueues(channel: Channel) { await channel.assertExchange(EXCHANGES.MAIN, "topic") await channel.assertQueue(QUEUES.OBJ) - await channel.assertQueue(QUEUES.DLX) - await channel.assertQueue(QUEUES.DEL, { + await channel.assertQueue(QUEUES.OBJDLX) + await channel.assertQueue(QUEUES.OBJDEL, { durable: true, arguments: { 'x-message-ttl': DELAY,