WEBINT-338_tiempo_suspension #2

Merged
alvarsanmartin merged 8 commits from WEBINT-338_tiempo_suspension into main 2026-04-28 13:40:04 +00:00
17 changed files with 243 additions and 39 deletions
Showing only changes of commit 2dba2ebfae - Show all commits

View File

@@ -0,0 +1,26 @@
meta {
name: France Suspended Lines
type: http
seq: 17
}
get {
url: {{baseurl}}/france/lines?status=SUSPENDED&limit=100
body: none
auth: inherit
}
params:query {
status: SUSPENDED
limit: 100
}
vars:pre-request {
iccid: 8933201125065160331
~baseurl: http://localhost:3002
}
settings {
encodeUrl: true
timeout: 0
}

View File

@@ -10,6 +10,7 @@ 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";
import { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js";
describe("SimController Integration Tests (Real UseCases)", () => {
let eventBusMock: any;
@@ -32,11 +33,13 @@ describe("SimController Integration Tests (Real UseCases)", () => {
);
const orderRepository = new OrderRepository(postgrClient);
const pauseRepository = new PauseCancelTaskRepository(postgrClient);
const linesRepository = new ObjeniousLinesRepository(postgrClient) // tiene que apuntar a "intranet"
useCases = new SimUseCases({
httpClient: httpInstance,
operationRepository: operationRepository,
orderRepository: orderRepository,
pauseRepository: pauseRepository
pauseRepository: pauseRepository,
objeniousLinesRepository: linesRepository
});
// @ts-expect-error
useCases.findActivationDate = async (data: ActionData) => new Date()

View File

@@ -4,6 +4,9 @@ import { SimUseCases } from "./Sim.usecases.js";
import { SimEvents } from "sim-shared/domain/SimEvents.js";
import { Result } from "sim-shared/domain/Result.js";
import { ActionData } from "#domain/DTOs/objeniousapi.js";
import { Request, Response } from "express"
import { PaginationArgs, QueryPaginationArgs } from "sim-shared/domain/PaginationArgs.js";
import { paginationValidator } from "./httpValidators.js";
/**
* La clase usa generadores de funciones para mantener el contexto
@@ -219,6 +222,36 @@ export class SimController {
}
}
public queryLines() {
const DEFAULT_LIMIT = 1000
const DEFAULT_OFFSET = 0
return async (req: Request, res: Response) => {
const queryParams = req.query
const paginationArgs: QueryPaginationArgs = {
limit: queryParams.limit as string | undefined,
offset: queryParams.offset as string | undefined
}
const validationRes = paginationValidator.validate(paginationArgs)
if (validationRes.error != undefined) {
res.status(402).json(validationRes)
return;
}
const paginationValues = {
limit: (queryParams.limit != undefined) ? Number(queryParams.limit) : DEFAULT_LIMIT,
offset: (queryParams.offset != undefined) ? Number(queryParams.offset) : DEFAULT_OFFSET
}
const status = req.query.status
const queryRes = await this.useCases.getLinesByQuery({ status: status as string | undefined }, paginationValues)
res.json(queryRes)
}
}
/**
* TODO:
* - Loguear motivos de la no validacion

View File

@@ -7,6 +7,9 @@ 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"
import { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js"
import { error } from "node:console"
import { ObjeniousLine, ObjeniousLineDb } from "sim-shared/domain/objeniousLine.js"
// TODO:
// - Pasar a un archivo de DTOs
@@ -17,17 +20,19 @@ export class SimUseCases {
private readonly objeniousRepository: ObjeniousOperationsRepository
private readonly orderRepository: OrderRepository
private readonly pauseRepository: PauseCancelTaskRepository
private readonly objeniousLinesRepository: ObjeniousLinesRepository
constructor(args: {
httpClient: HttpClient,
operationRepository: ObjeniousOperationsRepository,
orderRepository: OrderRepository,
pauseRepository: PauseCancelTaskRepository
pauseRepository: PauseCancelTaskRepository,
objeniousLinesRepository: ObjeniousLinesRepository
}) {
this.httpClient = args.httpClient
this.objeniousRepository = args.operationRepository
this.orderRepository = args.orderRepository
this.pauseRepository = args.pauseRepository
this.objeniousLinesRepository = args.objeniousLinesRepository
}
private async logOperation(data: ObjeniousOperation) {
@@ -119,7 +124,6 @@ export class SimUseCases {
const iccid = activationData.identifier.identifiers
// 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.networkStatus == "SUSPENDED") {
const res = await this.reActivate(activationData)()
return res;
@@ -278,7 +282,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)
//console.log("LineData", lineData.data)
if (lineData.error != undefined) {
console.error(lineData.error)
} else {
@@ -435,7 +439,7 @@ export class SimUseCases {
identifier: terminationData.identifier
},
url: OPERATION_URL,
iccid: terminationData.identifier.identifiers[0], //
iccid: terminationData.identifier.identifiers[0],
operation: "terminate"
})
}
@@ -443,7 +447,8 @@ export class SimUseCases {
/**
* Calcula el tiempo que una linea ha estado en suspensión
*/
public async getSuspendedTime(iccid: string): Promise<Result<string, { total_milliseconds: number, total_days: number }>> {
public async getSuspendedTime(iccid: string):
Promise<Result<string, { total_milliseconds: number, total_days: number }>> {
try {
const result = await this.objeniousRepository.getSuspendedTime(iccid);
if (result.error !== undefined) {
@@ -461,4 +466,26 @@ export class SimUseCases {
}
}
/**
* Busqueda de líneas **en nuestro volcado** según una query y con paginacion
*/
public async getLinesByQuery(query: { status?: string | undefined }, pagination: { limit: number, offset: number })
: Promise<Result<string, {
data: ObjeniousLineDb[],
offset: number,
rowCount: number
}>> {
try {
const linesQuery = await this.objeniousLinesRepository.getLinesByStatus(query, pagination)
return {
data: linesQuery,
}
} catch (e) {
return {
error: String(e)
}
}
}
}

View File

@@ -0,0 +1,18 @@
import { BodyValidator, Validator } from "sim-shared/aplication/BodyValidator.js";
import { QueryPaginationArgs } from "sim-shared/domain/PaginationArgs.js";
const limitPositiveOrUndefined = <Validator<QueryPaginationArgs>>{
field: "limit",
validationFunc: (args) => (args.limit == undefined || !isNaN(+args.limit) && parseInt(args.limit) >= 0),
errorMsg: "El campo limit debe ser un numero o undefined (default 0)"
}
const offsetPositiveOrUndefined = <Validator<QueryPaginationArgs>>{
field: "offset",
validationFunc: (args) => (args.offset == undefined || isNaN(+args.offset) && parseInt(args.offset) >= 1),
errorMsg: "El campo offset debe ser un numero o undefined (default 0)"
}
export const paginationValidator = new BodyValidator<QueryPaginationArgs & {}>([
limitPositiveOrUndefined,
offsetPositiveOrUndefined
])

View File

@@ -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 postgresClientIntranet = new PgClient({
pool: pgPoolIntranet
})

View File

@@ -14,6 +14,8 @@ import express from "express"
import cors from "cors"
import assert from "node:assert";
import { env } from "#config/env/index.js"
import { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js"
import { postgresClientIntranet } from "#config/intranetPostgresConfig.js"
async function startWorker() {
const rmqClient = await startRMQClient()
@@ -31,26 +33,29 @@ async function startWorker() {
const orderRepository = new OrderRepository(pgClient)
const pauseRepository = new PauseCancelTaskRepository(pgClient)
const linesRepository = new ObjeniousLinesRepository(
postgresClientIntranet
)
const simUseCases = new SimUseCases({
httpClient: httpClient,
operationRepository: operationRepository,
orderRepository: orderRepository,
pauseRepository: pauseRepository
pauseRepository: pauseRepository,
objeniousLinesRepository: linesRepository
})
const simActivationController = new SimController(
const simController = new SimController(
rmqClient,
simUseCases
)
const simRouter = new SimRouter(simActivationController, rmqClient)
const simRouter = new SimRouter(simController, rmqClient)
// de momento solo una cola por simplificar
rmqClient.consume("sim.objenious", simRouter.route)
// Servidor express
const port = env.PORT
console.log("PUERTO OBJ ", port)
const app = express()
app.use(cors())
app.use(express.json())
@@ -76,6 +81,24 @@ async function startWorker() {
res.json(result) // {data:{...}} || {error:{...}}
})
/**
* Opciones query:
* - state
*
* Respuestas:
* - OK: data: {
* lines: ObjeniousLineDb[],
* offset: number,
* rowCount: number
* }
*
* - ERR: error: {
* message: string
* }
*/
app.get("/lines", simController.queryLines())
assert.ok(port, "Puerto del servicio no definido")
app.listen(port, () => {
console.log(`[o] HTTP server listening on port ${port}`)

View File

@@ -13,10 +13,10 @@
"types": "./config/*.ts",
"default": "./config/*.js"
},
"#shared/*.js": {
"sim-shared/*.js": {
"default": "../sim-shared/*.js"
},
"#shared/*": {
"sim-shared/*": {
"default": "../sim-shared/*.js"
},
"#adapters/*.js": {

View File

@@ -1,8 +1,8 @@
import { BodyValidator } from "sim-shared/aplication/BodyValidator.js"
import { OrderUsecases } from "./Order.usecases.js"
import { Request, Response } from "express"
import { PaginationArgs } from "#domain/common.js"
import { idValidator, uuidValidator } from "./httpValidators.js"
import { PaginationArgs } from "sim-shared/domain/PaginationArgs.js"
export class OrderController {
private orderUseCases: OrderUsecases

View File

@@ -1,4 +1,4 @@
import { PaginationArgs } from "#domain/common.js";
import { PaginationArgs } from "sim-shared/domain/PaginationArgs.js";
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js";

View File

@@ -1,6 +0,0 @@
export type PaginationArgs = {
limit?: number,
offset?: number,
start?: number
}

View File

@@ -18,24 +18,16 @@ franceRoutes.use("", createProxyMiddleware({
on: {
proxyReq: (proxyReq: ClientRequest, req: Request) => {
/* Debug de las peticiones */
const protocol = req.protocol;
const host = req.get('host');
const originalFullUrl = `${protocol}://${host}${req.originalUrl}`;
const destinationFullUrl = `${FRANCE_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}`);
//console.log(`[Proxy Req]: ${req.method} ${req.url} -> ${proxyReq.path}`);
}
}
}
))
//orderRoutes.get("/:iccid/suspended-time",)
export { franceRoutes }

View File

@@ -6,7 +6,7 @@ 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 { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js"
import { postgresClientIntranet } from "./config/intranetPostgresConfig.js"
import { PauseCancelTaskRepository } from "sim-consumidor-objenious/infrastructure/PauseCancelTaskRepository.js"
import { PauseTerminateTask } from "./tasks/check_pause_terminate.js"
@@ -52,7 +52,8 @@ async function startCron() {
httpClient: httpClient,
operationRepository: operationRepository,
orderRepository: orderRepository,
pauseRepository: pauseRepo
pauseRepository: pauseRepo,
objeniousLinesRepository: objeniousLineRepository
})
const pauseTask = new PauseTerminateTask(
@@ -73,6 +74,7 @@ async function startCron() {
}, PERIODO_PETICIONES)
const PERIODO_VOLCADO = 60 * 60 * 1000
//await volcadoLineasTask.loadLines()
const volcadoInterval = setInterval(async () => {
try {
await volcadoLineasTask.loadLines()
@@ -81,7 +83,7 @@ async function startCron() {
}
}, PERIODO_VOLCADO)
await pauseTask.run()
// await pauseTask.run()
const PERIODO_CANCELACIONES = 60 * 60 * 1000;
const clacelacionesInterval = setInterval(async () => {
await pauseTask.run()

View File

@@ -1,5 +1,5 @@
import { lineToCreateLineDto, ObjeniousLine } from "sim-shared/domain/objeniousLine.js";
import { ObjeniousLinesRepository } from "../infranstructure/ObjeniousLinesRepository.js";
import { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js";
import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js";
export class TaskVolcadoLineas {

View File

@@ -0,0 +1,14 @@
/**
* Los parametros según entran por la query pueden ser de tipo string
*/
export type QueryPaginationArgs = {
limit?: string,
offset?: string,
start?: string// start = offset; Por compatibilidad, normalmente solo limit y offset
}
export type PaginationArgs = {
limit?: number,
offset?: number,
start?: number // start = offset; Por compatibilidad, normalmente solo limit y offset
}

View File

@@ -1,12 +1,12 @@
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";
import { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js";
import assert from "node:assert";
import { postgresClient } from "../config/config.test.js";
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 pgClient = postgresClient// 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,

View File

@@ -4,7 +4,7 @@
*/
import { createHash } from "node:crypto";
import { PoolClient } from "pg";
import { CreateObjeniousLineDTO } from "sim-shared/domain/objeniousLine.js";
import { CreateObjeniousLineDTO, ObjeniousLineDb } from "sim-shared/domain/objeniousLine.js";
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
export class ObjeniousLinesRepository {
@@ -24,6 +24,58 @@ export class ObjeniousLinesRepository {
}
}
/**
* Hay que hacer la query un poco mas general
*/
public async getLinesByStatus(query: { status?: string | undefined }, pagination: { limit: number, offset: number }) {
// $1 y $2 se reservan para paginación
const values: (string | number)[] = [
pagination.limit,
pagination.offset,
]
const paginationStr = `
LIMIT $1
OFFSET $2
`
const conditionsStr = `
WHERE
raw -> 'status' ->> 'networkStatus' = $3
`
let queryStr = `
SELECT * FROM objenious_lines
`
if (query.status != undefined) {
queryStr = queryStr + conditionsStr
values.push(query.status)
}
queryStr = queryStr + `
${paginationStr};
`
let client: PoolClient | undefined = undefined;
try {
client = await this.pgClient.connect();
const res = await client.query<ObjeniousLineDb>(queryStr, values);
return {
data: res.rows,
offset: pagination.offset,
rowCount: res.rowCount ?? 0,
}
} catch (err) {
console.error('Error en la query:', err);
throw err;
} finally {
if (client != undefined) {
client.release()
}
}
}
public async insertOrUpdate(data: CreateObjeniousLineDTO) {
const query = `
INSERT INTO objenious_lines (