Merge main -> migracion alai
This commit is contained in:
1
packages/_template/config/env/index.ts
vendored
1
packages/_template/config/env/index.ts
vendored
@@ -1,5 +1,4 @@
|
||||
export const env = {
|
||||
ENVIRONMENT: process.env.ENVIORMENT,
|
||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
||||
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
NOS_BASE_URL=localhost
|
||||
APP_PORT=3001
|
||||
APP_HOST="0.0.0.0"
|
||||
|
||||
ENVIORMENT=development
|
||||
|
||||
NOS_ACCESS_TOKEN=2YGhecTr4+uKbVKxaqBlk2edsrHA2OQY
|
||||
NOS_BASE_URL=https://nosconnectcenter-api.iot-x.com
|
||||
@@ -13,7 +13,6 @@ try {
|
||||
}
|
||||
|
||||
export const env = {
|
||||
ENVIRONMENT: process.env.ENVIORMENT,
|
||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
||||
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# claves de Objenious
|
||||
HOST=0.0.0.0
|
||||
|
||||
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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -269,7 +273,7 @@ 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
|
||||
* la actual para saber cuando se va a completar el periodo de test de una linea
|
||||
*/
|
||||
private async findActivationDate(actionData: ActionData) {
|
||||
const iccid = actionData.identifier.identifiers
|
||||
@@ -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 {
|
||||
@@ -321,13 +325,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)
|
||||
@@ -434,10 +439,53 @@ export class SimUseCases {
|
||||
identifier: terminationData.identifier
|
||||
},
|
||||
url: OPERATION_URL,
|
||||
iccid: terminationData.identifier.identifiers[0], //
|
||||
iccid: terminationData.identifier.identifiers[0],
|
||||
operation: "terminate"
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 }>> {
|
||||
try {
|
||||
const result = await this.objeniousRepository.getSuspendedTime(iccid);
|
||||
if (result.error !== undefined) {
|
||||
return { error: result.error as string, data: undefined };
|
||||
}
|
||||
return {
|
||||
data: {
|
||||
total_milliseconds: result.data!.total_milliseconds,
|
||||
total_days: result.data!.total_days
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("[Sim.usecases] Error getting suspended time", error);
|
||||
return { error: "Error getting suspended time", data: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
])
|
||||
@@ -4,7 +4,10 @@ import path from "node:path";
|
||||
loadEnvFile(path.join("../../.env")) // Global
|
||||
loadEnvFile(path.join("./.env")) // base
|
||||
|
||||
|
||||
export const env = {
|
||||
PORT: parseInt(process.env.OBJENIOUS_CONSUMER_PORT || "3002"),
|
||||
|
||||
ENVIRONMENT: process.env.ENVIORMENT,
|
||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
||||
|
||||
@@ -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
|
||||
})
|
||||
@@ -10,6 +10,13 @@ import { SimRouter } from "./aplication/Sim.router.js"
|
||||
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"
|
||||
import { PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js"
|
||||
|
||||
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()
|
||||
|
||||
@@ -26,20 +33,76 @@ async function startWorker() {
|
||||
const orderRepository = new OrderRepository(pgClient)
|
||||
|
||||
const pauseRepository = new PauseCancelTaskRepository(pgClient)
|
||||
|
||||
const simActivationController = new SimController(
|
||||
rmqClient,
|
||||
new SimUseCases({
|
||||
httpClient: httpClient,
|
||||
operationRepository: operationRepository,
|
||||
orderRepository: orderRepository,
|
||||
pauseRepository: pauseRepository
|
||||
})
|
||||
const linesRepository = new ObjeniousLinesRepository(
|
||||
postgresClientIntranet
|
||||
)
|
||||
const simRouter = new SimRouter(simActivationController, rmqClient)
|
||||
|
||||
const simUseCases = new SimUseCases({
|
||||
httpClient: httpClient,
|
||||
operationRepository: operationRepository,
|
||||
orderRepository: orderRepository,
|
||||
pauseRepository: pauseRepository,
|
||||
objeniousLinesRepository: linesRepository
|
||||
})
|
||||
|
||||
const simController = new SimController(
|
||||
rmqClient,
|
||||
simUseCases
|
||||
)
|
||||
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
|
||||
const app = express()
|
||||
app.use(cors())
|
||||
app.use(express.json())
|
||||
|
||||
app.get("/health", async (req, res) => {
|
||||
res.json({ ok: "true" })
|
||||
})
|
||||
|
||||
// TODO: meter el template de controller con los validadores
|
||||
app.get("/lines/:iccid/suspended-time", async (req, res) => {
|
||||
const iccid = req.params.iccid
|
||||
if (!iccid) {
|
||||
res.status(400).json({ error: "iccid is required" })
|
||||
return
|
||||
}
|
||||
|
||||
const result = await simUseCases.getSuspendedTime(iccid)
|
||||
if (result.error !== undefined) {
|
||||
res.status(500).json({ error: result.error })
|
||||
return
|
||||
}
|
||||
|
||||
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}`)
|
||||
})
|
||||
}
|
||||
|
||||
startWorker()
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PaginationArgs } from "#domain/common.js";
|
||||
import { OrderQuery } from "sim-shared/domain/Order.js";
|
||||
import { PaginationArgs } from "sim-shared/domain/PaginationArgs.js";
|
||||
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js";
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import path from "node:path";
|
||||
|
||||
|
||||
loadEnvFile(path.join("../../.env")) // Global
|
||||
//loadEnvFile(path.join("./.env")) // Global
|
||||
|
||||
export const env = {
|
||||
ENVIRONMENT: process.env.ENVIORMENT,
|
||||
@@ -22,5 +23,7 @@ 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)
|
||||
CONNECTIONS_URL: String(process.env.CONNECTIONS_URL),
|
||||
OBJENIOUS_CONSUMER_URL: process.env.OBJENIOUS_CONSUMER_URL,
|
||||
NOS_CONSUMER_URL: process.env.NOS_CONSUMER_URL,
|
||||
};
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
export type PaginationArgs = {
|
||||
limit?: number,
|
||||
offset?: number,
|
||||
start?: number
|
||||
}
|
||||
@@ -6,6 +6,7 @@ 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";
|
||||
import { franceRoutes } from "#adapters/franceRoutes.http.js";
|
||||
|
||||
const PORT = env.API_PORT
|
||||
const HOSTNAME = "0.0.0.0"
|
||||
@@ -31,6 +32,9 @@ app.use("/orders", orderRoutes)
|
||||
|
||||
app.use("/docs", express.static(path.join(process.cwd(), '../../docs')))
|
||||
|
||||
// Rutas especificas para casos especiales como el tiempo de suspension de francia
|
||||
app.use("/france", franceRoutes)
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.status(200).json({ status: "ok" })
|
||||
})
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Router, Request } from "express"
|
||||
import { ClientRequest, } from "http"
|
||||
import { createProxyMiddleware } from "http-proxy-middleware"
|
||||
import { env } from "#config/env/index.js"
|
||||
import assert from "node:assert"
|
||||
|
||||
const franceRoutes = Router()
|
||||
|
||||
const FRANCE_URL = env.OBJENIOUS_CONSUMER_URL
|
||||
assert.ok(FRANCE_URL, "No se ha definido una URL para el servicio consumidor de Francia")
|
||||
|
||||
franceRoutes.use("", createProxyMiddleware({
|
||||
target: FRANCE_URL,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
'^/france/*': '/'
|
||||
},
|
||||
|
||||
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(`[Proxy Req]: ${req.method} ${req.url} -> ${proxyReq.path}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
))
|
||||
|
||||
//orderRoutes.get("/:iccid/suspended-time",)
|
||||
export { franceRoutes }
|
||||
@@ -47,5 +47,3 @@ if (env.ENVIRONMENT == "production") {
|
||||
}
|
||||
|
||||
|
||||
console.log("[i] verificado env")
|
||||
|
||||
|
||||
@@ -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(
|
||||
@@ -62,7 +63,7 @@ async function startCron() {
|
||||
orderRepository
|
||||
)
|
||||
|
||||
await objTask.getPendingOperations()
|
||||
//await objTask.getPendingOperations()
|
||||
const PERIODO_PETICIONES = 10 * 60 * 1000
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
@@ -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()
|
||||
|
||||
@@ -37,6 +37,11 @@ export class CheckObjeniousRequests {
|
||||
// Todas las validas
|
||||
const operacionesValidas = pendingOperations.data
|
||||
.filter((e) => e.request_id != undefined)
|
||||
.filter((e) => e.operation != "terminate" && e.operation != "suspend")
|
||||
|
||||
// Filtrar suspension / terminacion
|
||||
|
||||
|
||||
// Validas sin MassId
|
||||
const solicitarMassId = operacionesValidas
|
||||
.filter((e) => e.mass_action_id == undefined)
|
||||
|
||||
@@ -66,6 +66,7 @@ export class PauseTerminateTask {
|
||||
}
|
||||
|
||||
while (!lines.done) {
|
||||
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)
|
||||
@@ -74,7 +75,6 @@ export class PauseTerminateTask {
|
||||
lineasActualizadas.push(...lines.value.data)
|
||||
}
|
||||
|
||||
lines = await lineGenerator.next()
|
||||
}
|
||||
|
||||
console.log("Cargado: ", lineasActualizadas)
|
||||
@@ -126,6 +126,7 @@ export class PauseTerminateTask {
|
||||
switch (operacionTipo) {
|
||||
case "suspend":
|
||||
result = await this.simUsecases.suspend(actionData)()
|
||||
console.log("SUSPENDED", result)
|
||||
break;
|
||||
case "terminate":
|
||||
result = await this.simUsecases.terminate(actionData)()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
14
packages/sim-shared/domain/PaginationArgs.ts
Normal file
14
packages/sim-shared/domain/PaginationArgs.ts
Normal 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
|
||||
}
|
||||
@@ -6,6 +6,7 @@ export interface IOperationsRepository {
|
||||
createOperation(data: ObjeniousOperation): Promise<Result<string, ObjeniousOperation>>
|
||||
updateOperation(data: ObjeniousOperationChange): Promise<Result<string, ObjeniousOperation>>
|
||||
getPendingOperations(): Promise<Result<string, ObjeniousOperation[]>>
|
||||
getSuspendedTime(iccid: string): Promise<Result<string, { total_milliseconds: number, total_days: number }>>
|
||||
}
|
||||
|
||||
export type ObjeniousOperation = {
|
||||
|
||||
@@ -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,
|
||||
@@ -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 (
|
||||
@@ -1,4 +1,4 @@
|
||||
import { before, describe, it } from "node:test";
|
||||
import { before, after, 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";
|
||||
@@ -21,6 +21,7 @@ describe("[Integration] Test API requests", () => {
|
||||
httpObjClient,
|
||||
postgresClient
|
||||
)
|
||||
const suspend_iccid = "test_suspended_time_iccid";
|
||||
|
||||
before(async () => {
|
||||
await repository.createOperation(correctOperation)
|
||||
@@ -36,4 +37,40 @@ describe("[Integration] Test API requests", () => {
|
||||
* - Se ignoran las erroneas
|
||||
*/
|
||||
})
|
||||
|
||||
it("Calculates suspended time accurately", async () => {
|
||||
// Se crean registros con un iccid concocido
|
||||
await postgresClient.query(`
|
||||
INSERT INTO objenious_operation (operation, iccids, status, error, start_date, end_date) VALUES
|
||||
('suspend', $1, 'finished', NULL, NOW() - INTERVAL '3 days', NOW() - INTERVAL '3 days'),
|
||||
('reactivate', $1, 'finished', NULL, NOW() - INTERVAL '2 days', NOW() - INTERVAL '2 days'),
|
||||
('suspend', $1, 'finished', NULL, NOW() - INTERVAL '1 day', NOW() - INTERVAL '1 day');
|
||||
`, [suspend_iccid]);
|
||||
|
||||
const result = await repository.getSuspendedTime(suspend_iccid);
|
||||
|
||||
if (result.error) {
|
||||
throw new Error("Query returned an error: " + result.error);
|
||||
}
|
||||
|
||||
// Se esperan mas o menos 2 dias para cada periodo, total 4 (Puede cambiar un poco por zona horaria)
|
||||
// 2 dias en ms
|
||||
const expectedApproxMs = 2 * 24 * 60 * 60 * 1000;
|
||||
const msDiff = Math.abs(result.data!.total_milliseconds - expectedApproxMs);
|
||||
|
||||
// Margen de 5s
|
||||
if (msDiff > 5000) {
|
||||
throw new Error(`Expected approx ${expectedApproxMs} ms, got ${result.data!.total_milliseconds}`);
|
||||
}
|
||||
|
||||
// como se incluye el dia de sespension los dias van a variar de 2 a 3
|
||||
if (result.data!.total_days < 2) {
|
||||
throw new Error("total_days should be at least 2");
|
||||
}
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
// Eliminacion de los iccid de periodo de suspensiones
|
||||
await postgresClient.query(`DELETE FROM objenious_operation WHERE iccids = '${suspend_iccid}'`);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -231,4 +231,66 @@ export class ObjeniousOperationsRepository implements IOperationsRepository {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene el tiempo en suspensión de una linea en miliseguntos y dias efetivos
|
||||
* Todo el calculo se hace en postgres. Puede que haga falta traer las transiciones
|
||||
* que normalmente son pocas, para hacer filtros personalizados.
|
||||
*/
|
||||
async getSuspendedTime(iccid: string): Promise<Result<string, { total_milliseconds: number, total_days: number }>> {
|
||||
const query = `
|
||||
WITH ordered_events AS (
|
||||
-- 1. Selecciona y normaliza los eventos relevantes del historial
|
||||
-- Se define el 'estado' final (suspendido vs activo) basado en la operación
|
||||
SELECT operation, end_date,
|
||||
CASE WHEN operation = 'suspend' THEN 'suspended' ELSE 'active' END as state
|
||||
FROM objenious_operation
|
||||
WHERE iccids = $1 AND status = 'finished' AND error IS NULL
|
||||
AND operation IN ('suspend', 'activate', 'reactivate', 'terminate')
|
||||
ORDER BY end_date
|
||||
),
|
||||
state_transitions AS (
|
||||
-- 2. Detecta cambios de estado comparando con la fila anterior (LAG)
|
||||
SELECT state, end_date,
|
||||
LAG(state) OVER (ORDER BY end_date) as prev_state
|
||||
FROM ordered_events
|
||||
),
|
||||
filtered_transitions AS (
|
||||
-- 3. Filtra solo las filas donde el estado realmente ha cambaido
|
||||
-- Se obtiene la fecha de inicio del siguiente intervalo (LEAD)
|
||||
SELECT state, end_date,
|
||||
LEAD(end_date) OVER (ORDER BY end_date) as next_end_date
|
||||
FROM state_transitions
|
||||
WHERE state IS DISTINCT FROM prev_state
|
||||
),
|
||||
intervals AS (
|
||||
-- 4. Calcula la duración de los periodos en los que el estado fue 'suspended'
|
||||
-- Se usa NOW() para el intervalo abierto (último estado hasta hoy)
|
||||
SELECT EXTRACT(EPOCH FROM (COALESCE(next_end_date, NOW() at time zone 'utc') - end_date)) * 1000 as ms_duration,
|
||||
(COALESCE(next_end_date, NOW() at time zone 'utc')::date - end_date::date) + 1 as days_duration
|
||||
FROM filtered_transitions
|
||||
WHERE state = 'suspended'
|
||||
)
|
||||
-- 5. Suma total de tiempo en estado de suspensión
|
||||
SELECT COALESCE(SUM(ms_duration)::bigint, 0) as total_milliseconds,
|
||||
COALESCE(SUM(days_duration), 0) as total_days
|
||||
FROM intervals;
|
||||
`;
|
||||
|
||||
try {
|
||||
const { rows } = await this.pgClient.query<{ total_milliseconds: string, total_days: string }>(query, [iccid]);
|
||||
return {
|
||||
data: {
|
||||
total_milliseconds: parseFloat(rows[0].total_milliseconds),
|
||||
total_days: parseInt(rows[0].total_days)
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
console.error("Error calculating suspended time", e);
|
||||
return {
|
||||
error: String(e),
|
||||
data: undefined
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ const order2 = <CreateOrderDTO>{
|
||||
payload: { iccid: "5678", action: "activate" }
|
||||
}
|
||||
|
||||
|
||||
describe("Test OrderRepository", {}, (ctx) => {
|
||||
const orderRepo = new OrderRepository(postgresClient)
|
||||
let testIds: number[] = []
|
||||
|
||||
Reference in New Issue
Block a user