Compare commits
37 Commits
1.1.1
...
alarmas-ob
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c2e86779a | |||
| 1780cf1743 | |||
| 10104c202f | |||
| 1b6da651a6 | |||
| 9b305f887f | |||
| 9506b9e28e | |||
| 61c0edca07 | |||
| 9470b5605d | |||
| 9d63d23754 | |||
| a95655a2a6 | |||
| 025801a689 | |||
| 28880c4d99 | |||
| ef8222ae30 | |||
| 5bb3bc554b | |||
| cfb907b840 | |||
| d5d7953fd2 | |||
| 96298aab25 | |||
| c17cca1e81 | |||
| 7264efcf79 | |||
| 8934bcd603 | |||
| bdd08dbc56 | |||
| 7d47fde806 | |||
| ad207fb732 | |||
| bd9081b5bc | |||
| a429e9d14a | |||
| 81eb986313 | |||
| 58bedc42f1 | |||
| b97f422261 | |||
| 7a7dc33724 | |||
| 7743bd1f0d | |||
| 2897d7aa3c | |||
| 0fd7eafcf3 | |||
| 71253d216e | |||
| aeea6cfefd | |||
| e8eb925834 | |||
| 7cf9cc60e6 | |||
| 1e9818d430 |
@@ -3,3 +3,9 @@ compressionLevel: mixed
|
||||
enableGlobalCache: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
npmScopes:
|
||||
sf-alvar:
|
||||
npmRegistryServer: "https://git.savefamilygps.net/api/packages/SaveFamily/npm/"
|
||||
|
||||
npmRegistryServer: "https://registry.npmjs.org/"
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
#/bin/bash
|
||||
rm deployment/database/init.sql
|
||||
# cat deployment/database/*.sql >deployment/database/init.sql
|
||||
cp deployment/database/esquema_final* deployment/database/init.sql
|
||||
|
||||
# compatibilidad con postgresql < 17
|
||||
sed -i '/\\restrict/d' deployment/database/init.sql
|
||||
sed -i '/\\unrestrict/d' deployment/database/init.sql
|
||||
|
||||
docker compose -f deployment/local/docker/docker-compose.yaml --project-directory ./ build
|
||||
|
||||
20
deployment/database/base/xx-volcado-objenious.sql
Normal file
20
deployment/database/base/xx-volcado-objenious.sql
Normal file
@@ -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,
|
||||
preactivationDate TIMESTAMPTZ, -- No viene con hora
|
||||
activationDate TIMESTAMPTZ,
|
||||
commercialStatus TEXT,
|
||||
commercialStatusDate TIMESTAMPTZ,
|
||||
billingStatus TEXT,
|
||||
billingStatusChangeDate TIMESTAMPTZ,
|
||||
billingActivationDate TIMESTAMPTZ,
|
||||
createDate TIMESTAMPTZ,
|
||||
raw JSONB,
|
||||
hash TEXT
|
||||
)
|
||||
@@ -6,12 +6,11 @@ WORKDIR /home/node/app
|
||||
RUN corepack enable
|
||||
|
||||
COPY ./dist/packages ./packages
|
||||
COPY ./.yarnrc.yml ./
|
||||
COPY ./docs ./docs
|
||||
|
||||
COPY ./package.json ./
|
||||
|
||||
# Force node-modules linker (no .yarnrc.yml in build context)
|
||||
RUN echo 'nodeLinker: node-modules' > .yarnrc.yml
|
||||
|
||||
RUN yarn install
|
||||
|
||||
RUN mkdir -p dist && ln -sf ../packages dist/packages
|
||||
|
||||
@@ -17,12 +17,12 @@ pipeline {
|
||||
sh 'npm install -g yarn'
|
||||
sh 'corepack enable'
|
||||
sh 'corepack prepare yarn@4.12.0 --activate'
|
||||
sh 'yarn install'
|
||||
sh 'yarn install --immutable'
|
||||
}
|
||||
}
|
||||
stage("🧱 Building") {
|
||||
steps {
|
||||
sh 'rm -rf dist/'
|
||||
sh 'rm -rf dist/'
|
||||
sh 'yarn run build'
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,11 @@ pipeline {
|
||||
sourceFiles: "dist/**/*",
|
||||
excludes: "dist/**/node_modules/**"
|
||||
),
|
||||
sshTransfer(
|
||||
cleanRemote: false,
|
||||
remoteDirectory: "$APP_REMOTE_PATH",
|
||||
sourceFiles: "docs/**/*",
|
||||
),
|
||||
sshTransfer(
|
||||
cleanRemote: false,
|
||||
remoteDirectory: "$APP_REMOTE_PATH",
|
||||
@@ -88,6 +93,11 @@ pipeline {
|
||||
remoteDirectory: "$APP_REMOTE_PATH",
|
||||
sourceFiles: "package.json",
|
||||
),
|
||||
sshTransfer(
|
||||
cleanRemote: false,
|
||||
remoteDirectory: "$APP_REMOTE_PATH",
|
||||
sourceFiles: ".yarnrc.yml",
|
||||
),
|
||||
sshTransfer(
|
||||
cleanRemote: false,
|
||||
execCommand: "sh $APP_REMOTE_PATH/rebuild.sh"
|
||||
|
||||
@@ -14,6 +14,7 @@ COPY ./packages ./packages
|
||||
COPY tsconfig*.json ./
|
||||
COPY .env* ./
|
||||
COPY ./.yarnrc.yml ./
|
||||
COPY ./docs ./docs
|
||||
COPY ./deployment/local/docker/start.sh ./
|
||||
# Copiar el archivo de migrations? porque ahora no creo que se esté lanzando nada
|
||||
COPY ./deployment/database/migrations ./deployment/database/migrations
|
||||
|
||||
@@ -40,6 +40,9 @@ services:
|
||||
- path: ./packages
|
||||
action: sync
|
||||
target: /usr/local/app/packages
|
||||
- path: ./docs
|
||||
action: sync
|
||||
target: /usr/local/app/docs
|
||||
- path: ./package.json
|
||||
action: rebuild
|
||||
ports:
|
||||
@@ -72,7 +75,6 @@ services:
|
||||
- "${POSTGRES_PORT}:${POSTGRES_PORT}"
|
||||
volumes:
|
||||
- ./sql-data/:/var/lib/postgres/data
|
||||
- ./deployment/database/init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
|
||||
25
docs/sim-api-documentation.html
Normal file
25
docs/sim-api-documentation.html
Normal file
File diff suppressed because one or more lines are too long
16
docs/sim-api/Docs.bru
Normal file
16
docs/sim-api/Docs.bru
Normal file
@@ -0,0 +1,16 @@
|
||||
meta {
|
||||
name: Docs
|
||||
type: http
|
||||
seq: 12
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{baseurl}}/docs/sim-api-documentation.html
|
||||
body: none
|
||||
auth: inherit
|
||||
}
|
||||
|
||||
settings {
|
||||
encodeUrl: true
|
||||
timeout: 0
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -37,7 +37,7 @@ body:form-urlencoded {
|
||||
}
|
||||
|
||||
vars:pre-request {
|
||||
params.id: 14557
|
||||
params.id: 15102
|
||||
}
|
||||
|
||||
settings {
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
"migrate": "yarn db-migrate -e .env -m deployment/database/migrations -t 99.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sf-alvar/db-migrate": "1.0.3",
|
||||
"@tsconfig/node22": "^22.0.5",
|
||||
"amqp-connection-manager": "^5.0.0",
|
||||
"amqplib": "^0.10.9",
|
||||
"axios": "^1.13.3",
|
||||
"cors": "^2.8.5",
|
||||
"db-migrate": "http://gitea:3000/alvarsanmartin/herramienta-migracion.git",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.2.1",
|
||||
"pg": "^8.18.0",
|
||||
|
||||
1
packages/sim-alarmas-objenious/.env
Normal file
1
packages/sim-alarmas-objenious/.env
Normal file
@@ -0,0 +1 @@
|
||||
PORT=3001
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Request, Response } from "express"
|
||||
|
||||
export class AlarmsController {
|
||||
constructor() {
|
||||
// Alarm usecases
|
||||
}
|
||||
|
||||
public recibe() {
|
||||
return (req: Request, res: Response) => {
|
||||
const body = req.body()
|
||||
const header = req.headers
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
14
packages/sim-alarmas-objenious/aplication/Alarms.usecases.ts
Normal file
14
packages/sim-alarmas-objenious/aplication/Alarms.usecases.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
|
||||
|
||||
export class AlarmUsecases {
|
||||
constructor(
|
||||
private alarmRepository: any
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public saveAlarm() {
|
||||
|
||||
}
|
||||
}
|
||||
23
packages/sim-alarmas-objenious/config/env/index.ts
vendored
Normal file
23
packages/sim-alarmas-objenious/config/env/index.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import { loadEnvFile } from "node:process";
|
||||
loadEnvFile("../../.env")
|
||||
|
||||
export const env = {
|
||||
PORT: Number(process.env.PORT),
|
||||
ENVIRONMENT: process.env.ENVIORMENT,
|
||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
||||
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
||||
POSTGRES_HOST: process.env.POSTGRES_HOST,
|
||||
POSTGRES_DATABASE: process.env.POSTGRES_DATABASE,
|
||||
RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"),
|
||||
RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "test"),
|
||||
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "test"),
|
||||
RABBITMQ_EXCHANGE: String(process.env.RABBITMQ_EXCHANGE ?? "/"),
|
||||
RABBITMQ_PORT: parseInt(process.env.RABBITMQ_PORT ?? "5672"),
|
||||
RABBITMQ_MODULENAME: process.env.MODULENAME,
|
||||
RABBITMQ_TTL: process.env.RABBITMQ_TTL,
|
||||
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
||||
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
||||
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
||||
};
|
||||
|
||||
18
packages/sim-alarmas-objenious/config/postgreConfig.ts
Normal file
18
packages/sim-alarmas-objenious/config/postgreConfig.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Pool } from 'pg';
|
||||
import { PgClient } from 'sim-shared/infrastructure/PgClient.js'
|
||||
import { env } from './env/index.js';
|
||||
|
||||
// Configuracion de la conexion a la BDD, deberia ser la
|
||||
// Misma para todos los servicios pero hasta que se unifique todo
|
||||
// se hace una por servicio.
|
||||
export const pgPool = new Pool({
|
||||
user: env.POSTGRES_USER,
|
||||
host: env.POSTGRES_HOST,
|
||||
database: env.POSTGRES_DATABASE,
|
||||
password: env.POSTGRES_PASSWORD,
|
||||
port: Number(env.POSTGRES_PORT) || 5432,
|
||||
});
|
||||
|
||||
export const postgresClient = new PgClient({
|
||||
pool: pgPool
|
||||
})
|
||||
62
packages/sim-alarmas-objenious/domain/alarmTypes.ts
Normal file
62
packages/sim-alarmas-objenious/domain/alarmTypes.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
export type ObjeniousAlarmType =
|
||||
"PLMN_CHANGE" |
|
||||
"COUNTRY_CHANGE" |
|
||||
"OVER_CONSUMPTION_VOLUME" |
|
||||
"UNDER_CONSUMPTION_VOLUME" |
|
||||
"IMEI_CHANGE" |
|
||||
"OVER_CONSUMPTION_VOLUME" |
|
||||
string
|
||||
|
||||
export type ObjeniousAlarm = {
|
||||
alarmId: Number,
|
||||
emissionDate: Date //"07/02/2019 14:44:00"
|
||||
alarmType: ObjeniousAlarmType,
|
||||
lineInfos: {
|
||||
msisdn: string,
|
||||
iccid: string
|
||||
},
|
||||
change?: ObjeniousChangeTypes,
|
||||
usage?: ObjeniousUsageTypes
|
||||
}
|
||||
|
||||
export type ObjeniousChangeTypes = ChangePayload | ImeiChangePayload;
|
||||
export type ObjeniousUsageTypes = UsageAlarmPayload | OverConsumptionPayload;
|
||||
|
||||
/**
|
||||
* PLMN_CHANGE
|
||||
* COUNTRY_CHANGE
|
||||
* STATUS_CHANGE
|
||||
* */
|
||||
export type ChangePayload = {
|
||||
previous: string,
|
||||
current: string
|
||||
}
|
||||
|
||||
/**
|
||||
* IMEI_CHANGE
|
||||
*/
|
||||
export type ImeiChangePayload = {
|
||||
previous: string,//"42947662490276",
|
||||
current: string, //"74845559130697",
|
||||
previousManufacturer: string, //"u-blox AG",
|
||||
currentManufacturer: string, //"SierraWireless"
|
||||
}
|
||||
|
||||
/**
|
||||
* OVER_CONSUMPTION_VOLUME
|
||||
* UNDER_CONSUMPTION_VOLUME
|
||||
*/
|
||||
export type UsageAlarmPayload = {
|
||||
type: string,
|
||||
threshold: string,
|
||||
trigerValue: string
|
||||
}
|
||||
|
||||
/**
|
||||
* OVER_CONSUMPTION_VOLUME
|
||||
*/
|
||||
export type OverConsumptionPayload = {
|
||||
type: string, //"data-sms-smsIN-voiceOUT",
|
||||
threshold: string, // "500-200-100-10",
|
||||
triggerValue: string, // "600-300-101-15"
|
||||
}
|
||||
29
packages/sim-alarmas-objenious/index.ts
Normal file
29
packages/sim-alarmas-objenious/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import express from "express"
|
||||
import cors from 'cors';
|
||||
import { env } from "#config/env/index.js"
|
||||
import { AlarmsController } from "aplication/Alarms.controller";
|
||||
|
||||
const PORT = env.PORT
|
||||
const HOSTNAME = "0.0.0.0"
|
||||
const app = express()
|
||||
|
||||
// Instancias
|
||||
const alarmsController = new AlarmsController();
|
||||
|
||||
// Middleware
|
||||
app.use(cors());
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
app.use("/fr/alarms", alarmsController.recibe())
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.status(200).json({ status: "ok" })
|
||||
})
|
||||
|
||||
app.listen(PORT, HOSTNAME, () => {
|
||||
console.log("[o] Servidor iniciado en el puerto %d", PORT)
|
||||
})
|
||||
|
||||
export default {}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
|
||||
|
||||
export class AlarmsRepository {
|
||||
constructor(private pgClient: PgClient) {
|
||||
}
|
||||
|
||||
public createAlarm() {
|
||||
|
||||
}
|
||||
}
|
||||
76
packages/sim-alarmas-objenious/package.json
Normal file
76
packages/sim-alarmas-objenious/package.json
Normal file
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "sim-alarmas-objenious",
|
||||
"version": "1.0.0",
|
||||
"description": "Recibe las alarmas de los webhook de objenious",
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
"test": "node --import tsx --test ./**/*.test.ts",
|
||||
"build": "tsc --build && tsc-alias -p tsconfig.json && cp package.json ../../dist/packages/sim-alarmas-objenious/",
|
||||
"dev": "tsx watch index.ts",
|
||||
"start": "node ../../dist/packages/sim-alarmas-objenious/index.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"packageManager": "yarn@4.12.0",
|
||||
"imports": {
|
||||
"#config/*.js": {
|
||||
"types": "./config/*.ts",
|
||||
"default": "./config/*.js"
|
||||
},
|
||||
"#config/*": {
|
||||
"types": "./config/*.ts",
|
||||
"default": "./config/*.js"
|
||||
},
|
||||
"#adapters/*.js": {
|
||||
"types": "./adapters/*.ts",
|
||||
"default": "./adapters/*.js"
|
||||
},
|
||||
"#adapters/*": {
|
||||
"types": "./adapters/*.ts",
|
||||
"default": "./adapters/*.js"
|
||||
},
|
||||
"#domain/*.js": {
|
||||
"types": "./domain/*.ts",
|
||||
"default": "./domain/*.js"
|
||||
},
|
||||
"#domain/*": {
|
||||
"types": "./domain/*.ts",
|
||||
"default": "./domain/*.js"
|
||||
},
|
||||
"#ports/*.js": {
|
||||
"types": "./ports/*.ts",
|
||||
"default": "./ports/*.js"
|
||||
},
|
||||
"#ports/*": {
|
||||
"types": "./ports/*.ts",
|
||||
"default": "./ports/*.js"
|
||||
},
|
||||
"#tests/*.js": {
|
||||
"types": "./__tests__/*.ts",
|
||||
"default": "./__tests__/*.js"
|
||||
},
|
||||
"#tests/*": {
|
||||
"types": "./__tests__/*.ts",
|
||||
"default": "./__tests__/*.js"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@tsconfig/node22": "*",
|
||||
"amqplib": "^0.10.9",
|
||||
"cors": "*",
|
||||
"dotenv": "*",
|
||||
"express": "*",
|
||||
"typescript": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/amqplib": "^0.10.8",
|
||||
"@types/cors": "*",
|
||||
"@types/express": "*",
|
||||
"@types/node": "*",
|
||||
"@types/supertest": "*",
|
||||
"prettier": "*",
|
||||
"supertest": "*",
|
||||
"tsx": "*",
|
||||
"vitest": "*"
|
||||
}
|
||||
}
|
||||
17
packages/sim-alarmas-objenious/tsconfig.json
Normal file
17
packages/sim-alarmas-objenious/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist",
|
||||
"baseUrl": ".",
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"files": [
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
@@ -86,7 +86,7 @@ export class SimController {
|
||||
const resp = await this.tryUseCase(msg, this.useCases.activate({
|
||||
correlation_id: msgData.headers?.message_id,
|
||||
dueDate: this.genDueDate(DUE_DATE_SECONDS).toISOString(),
|
||||
customerAccountCode: env.OBJ_CUSTOMER_CODE,
|
||||
customerAccountCode: "9.49411.10",
|
||||
identifier: {
|
||||
identifierType: "ICCID",
|
||||
identifiers: [iccid]
|
||||
|
||||
@@ -110,7 +110,10 @@ export class SimUseCases {
|
||||
const OPERATION_URL = "/actions/activateLine"
|
||||
return async () => {
|
||||
const req = this.httpClient.client.post(OPERATION_URL, {
|
||||
...activationData
|
||||
dueDate: activationData.dueDate,
|
||||
identifier: activationData.identifier,
|
||||
customerAccountCode: activationData.customerAccountCode,
|
||||
offer: activationData.offer
|
||||
})
|
||||
|
||||
try {
|
||||
@@ -225,7 +228,10 @@ export class SimUseCases {
|
||||
const OPERATION_URL = "/actions/suspendLine"
|
||||
return this.generateUseCase({
|
||||
correlation_id: suspendData.correlation_id,
|
||||
operationPayload: suspendData,
|
||||
operationPayload: {
|
||||
dueDate: suspendData.dueDate,
|
||||
identifier: suspendData.identifier
|
||||
},
|
||||
url: OPERATION_URL,
|
||||
iccid: suspendData.identifier.identifiers[0], //
|
||||
operation: "suspend"
|
||||
@@ -236,7 +242,10 @@ export class SimUseCases {
|
||||
const OPERATION_URL = "/actions/terminateLine"
|
||||
return this.generateUseCase({
|
||||
correlation_id: terminationData.correlation_id,
|
||||
operationPayload: terminationData,
|
||||
operationPayload: {
|
||||
dueDate: terminationData.dueDate,
|
||||
identifier: terminationData.identifier
|
||||
},
|
||||
url: OPERATION_URL,
|
||||
iccid: terminationData.identifier.identifiers[0], //
|
||||
operation: "terminate"
|
||||
|
||||
@@ -79,7 +79,7 @@ export class SimController {
|
||||
...usecaseResult.error
|
||||
}
|
||||
}).send()
|
||||
args.onError(body, usecaseResult.error.msg.message)
|
||||
args.onError(body, usecaseResult.error ?? "Error indefinido")
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ export class SimUsecases {
|
||||
* alias de bloquear / suspender en objenious
|
||||
*/
|
||||
async pause(args: { iccid: string, compañia: string }):
|
||||
Promise<Result<string, { iccid: string, message_id: string, operation: "cancelation" }>> {
|
||||
Promise<Result<string, { iccid: string, message_id: string, operation: "pause" }>> {
|
||||
const pauseEvent = <SimEvents.pause>{
|
||||
key: `sim.${args.compañia}.pause`,
|
||||
payload: {
|
||||
@@ -222,7 +222,7 @@ export class SimUsecases {
|
||||
data: {
|
||||
iccid: args.iccid,
|
||||
message_id: savedOrder.data.correlation_id,
|
||||
operation: "cancelation"
|
||||
operation: "pause"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import express from "express"
|
||||
import cors from 'cors';
|
||||
import path from 'path';
|
||||
import { simRoutes } from "./infrastructure/simRoutes.http.js"
|
||||
import { rabbitmqEventBus } from '#config/eventBusConfig.js';
|
||||
import { env } from "#config/env/index.js"
|
||||
@@ -27,6 +28,8 @@ app.use(express.urlencoded({ extended: true }));
|
||||
app.use("/sim", simRoutes)
|
||||
app.use("/orders", orderRoutes)
|
||||
|
||||
app.use("/docs", express.static(path.join(process.cwd(), '../../docs')))
|
||||
|
||||
app.get("/health", (req, res) => {
|
||||
res.status(200).json({ status: "ok" })
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
10
packages/sim-objenious-cron/config/env/index.ts
vendored
10
packages/sim-objenious-cron/config/env/index.ts
vendored
@@ -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")
|
||||
|
||||
|
||||
20
packages/sim-objenious-cron/config/intranetPostgresConfig.ts
Normal file
20
packages/sim-objenious-cron/config/intranetPostgresConfig.ts
Normal 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
|
||||
})
|
||||
@@ -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 * 1000
|
||||
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()
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
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 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
|
||||
}
|
||||
|
||||
// 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")
|
||||
})
|
||||
|
||||
test("Should not update a line if the hash is the same", async () => {
|
||||
const res = await lineRepository.insertOrUpdate(lineaTest)
|
||||
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")
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* 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";
|
||||
|
||||
export class ObjeniousLinesRepository {
|
||||
constructor(
|
||||
private pgClient: PgClient
|
||||
) {
|
||||
}
|
||||
|
||||
private generateLineHash(data: CreateObjeniousLineDTO) {
|
||||
try {
|
||||
const lineStr = JSON.stringify(data)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
hash
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
|
||||
)
|
||||
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,
|
||||
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,
|
||||
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
|
||||
lineHash
|
||||
];
|
||||
|
||||
let client: PoolClient | undefined = undefined;
|
||||
try {
|
||||
client = await this.pgClient.connect();
|
||||
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;
|
||||
} finally {
|
||||
if (client != undefined) {
|
||||
client.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,8 +31,8 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc --build && tsc-alias -p tsconfig.json && cp package.json ../../dist/packages/sim-objenious-cron/",
|
||||
"test": "node --import tsx --test ./**/*.test.ts",
|
||||
"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"
|
||||
},
|
||||
|
||||
@@ -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,11 +50,14 @@ 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")
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -119,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])
|
||||
@@ -136,7 +137,7 @@ export class CheckObjeniousRequests {
|
||||
})
|
||||
}
|
||||
|
||||
if (originalAction.operation == "activation") {
|
||||
if (originalAction.operation == "activate") {
|
||||
this.notifyFinalization({
|
||||
...originalAction,
|
||||
msisdn
|
||||
@@ -215,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;
|
||||
|
||||
@@ -228,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 = {
|
||||
@@ -248,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;
|
||||
}
|
||||
}
|
||||
@@ -262,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]
|
||||
@@ -270,7 +310,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)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
133
packages/sim-objenious-cron/tasks/volcado_lineas.ts
Normal file
133
packages/sim-objenious-cron/tasks/volcado_lineas.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
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 = 100
|
||||
|
||||
export class TaskVolcadoLineas {
|
||||
constructor(
|
||||
private readonly httpClient: HttpClient,
|
||||
private readonly linesRepository: ObjeniousLinesRepository,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Mover al repo
|
||||
*/
|
||||
private async * getLinesByStatus(args?: {
|
||||
pageSize?: number,
|
||||
pageNumber?: number,
|
||||
status?: string
|
||||
}): AsyncGenerator<Result<string, ObjeniousLine[]>, Result<string, ObjeniousLine[]>, 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<string, string | number> = {}
|
||||
|
||||
const loadNextLine = async (page: number): Promise<Result<string, ObjeniousLine[]>> => {
|
||||
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<AxiosResponse<ObjeniousLineResponse>>(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)
|
||||
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")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
144
packages/sim-shared/domain/objeniousLine.ts
Normal file
144
packages/sim-shared/domain/objeniousLine.ts
Normal file
@@ -0,0 +1,144 @@
|
||||
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?: number;
|
||||
iccid: string;
|
||||
msisdn?: string;
|
||||
imei?: string;
|
||||
imeiChangeDate?: Date;
|
||||
offerCode?: string;
|
||||
status?: string;
|
||||
preactivationDate?: Date | null;
|
||||
activationDate?: Date | null;
|
||||
commercialStatus?: string;
|
||||
commercialStatusDate?: Date | null;
|
||||
billingStatus?: string;
|
||||
billingStatusChangeDate?: Date | null;
|
||||
billingActivationDate?: Date | null;
|
||||
createDate?: Date | null;
|
||||
raw: ObjeniousLine;
|
||||
}
|
||||
|
||||
// DTO para inserción (omite el ID autogenerado)
|
||||
export type CreateObjeniousLineDTO = Omit<ObjeniousLineDb, 'id'>;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
|
||||
@@ -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<Result<string, OrderTracking<any>>> {
|
||||
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<OrderTracking<any>>(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)
|
||||
)
|
||||
|
||||
@@ -1,12 +1,3 @@
|
||||
#/bin/bash
|
||||
rm deployment/database/init.sql
|
||||
|
||||
# init sql debe juntar todos los scripts de "base" (sin contar migraciones)
|
||||
cat deployment/database/base/*.sql >deployment/database/init.sql
|
||||
#cp deployment/database/esquema_final* deployment/database/init.sql
|
||||
|
||||
# compatibilidad con postgresql < 17
|
||||
sed -i '/\\restrict/d' deployment/database/init.sql
|
||||
sed -i '/\\unrestrict/d' deployment/database/init.sql
|
||||
|
||||
docker compose -f deployment/local/docker/docker-compose.yaml --project-directory ./ up --watch
|
||||
|
||||
32
yarn.lock
32
yarn.lock
@@ -452,6 +452,18 @@ __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"
|
||||
dependencies:
|
||||
pg: "npm:^8.18.0"
|
||||
yargs: "npm:^18.0.0"
|
||||
bin:
|
||||
db-migrate: lib/index.js
|
||||
checksum: 10/2b5745a5ce60456fc7fee1e6a8580978a520fedd8abbbc695557847cdf2b36aa5e1d795721ad35bc151fc9373dfa023bde73d6f43ba412b17293a1822c09fe6b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@standard-schema/spec@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "@standard-schema/spec@npm:1.1.0"
|
||||
@@ -1142,18 +1154,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"db-migrate@http://gitea:3000/alvarsanmartin/herramienta-migracion.git":
|
||||
version: 1.1.0
|
||||
resolution: "db-migrate@http://gitea:3000/alvarsanmartin/herramienta-migracion.git#commit=f84d68ba79161b9b06b747919979db00aac34b49"
|
||||
dependencies:
|
||||
pg: "npm:^8.18.0"
|
||||
yargs: "npm:^18.0.0"
|
||||
bin:
|
||||
db-migrate: ./lib/index.js
|
||||
checksum: 10/2468cfd14a5f218845f5437f530a68993a51b3998cdd9d0c7f28cdb810314200c471debac9ca19c34d4978907b7c4ced5c95e777eebd40c3baa795ad945d8892
|
||||
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":
|
||||
version: 4.4.3
|
||||
resolution: "debug@npm:4.4.3"
|
||||
@@ -2372,13 +2372,13 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"postcss@npm:^8.5.6":
|
||||
version: 8.5.6
|
||||
resolution: "postcss@npm:8.5.6"
|
||||
version: 8.5.8
|
||||
resolution: "postcss@npm:8.5.8"
|
||||
dependencies:
|
||||
nanoid: "npm:^3.3.11"
|
||||
picocolors: "npm:^1.1.1"
|
||||
source-map-js: "npm:^1.2.1"
|
||||
checksum: 10/9e4fbe97574091e9736d0e82a591e29aa100a0bf60276a926308f8c57249698935f35c5d2f4e80de778d0cbb8dcffab4f383d85fd50c5649aca421c3df729b86
|
||||
checksum: 10/cbacbfd7f767e2c820d4bf09a3a744834dd7d14f69ff08d1f57b1a7defce9ae5efcf31981890d9697a972a64e9965de677932ef28e4c8ba23a87aad45b82c459
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -2856,6 +2856,7 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "sim-eventos@workspace:."
|
||||
dependencies:
|
||||
"@sf-alvar/db-migrate": "npm:1.0.3"
|
||||
"@tsconfig/node22": "npm:^22.0.5"
|
||||
"@types/amqplib": "npm:^0.10.8"
|
||||
"@types/cors": "npm:^2.8.19"
|
||||
@@ -2868,7 +2869,6 @@ __metadata:
|
||||
axios: "npm:^1.13.3"
|
||||
concurrently: "npm:^9.2.1"
|
||||
cors: "npm:^2.8.5"
|
||||
db-migrate: "http://gitea:3000/alvarsanmartin/herramienta-migracion.git"
|
||||
dotenv: "npm:^17.2.3"
|
||||
express: "npm:^5.2.1"
|
||||
pg: "npm:^8.18.0"
|
||||
|
||||
Reference in New Issue
Block a user