Repositorio para llevar el registro de operaciones a objenious

This commit is contained in:
2026-02-04 15:28:33 +01:00
parent 6171959ce6
commit 5e99ea084a
12 changed files with 178 additions and 8 deletions

View File

@@ -1,2 +1,4 @@
#/bin/bash
rm deployment/database/init.sql
cat deployment/database/*.sql >deployment/database/init.sql
docker compose -f deployment/local/docker/docker-compose.yaml --project-directory ./ build

View File

@@ -7,7 +7,7 @@ drop domain if exists imsi_type cascade;
CREATE DOMAIN imsi_type as varchar(15);
CREATE table if not exists sim_cards (
CREATE table if not exists sim_car (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
imei imei_type,
iccid iccid_type,

View File

@@ -0,0 +1,42 @@
CREATE TYPE status_enum AS ENUM ('noRequestID','noMassID','running','finished','error','other');
-- Tabla para gestionar las peticiones de cambio de objenious.
-- Para una o mas lineas se pueden lanzar operacione que no sabemos
-- con certeza cuando van a terminar.
CREATE TABLE if not exists objenious_operation (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
retry_count INT DEFAULT 0,
max_retry INT DEFAULT 5,
max_date_retry TIMESTAMP DEFAULT NULL,
iccids TEXT,
request_id TEXT,
mass_action_id TEXT,
operation TEXT NOT NULL,
start_date TIMESTAMP NOT NULL DEFAULT now(),
last_change_date TIMESTAMP NOT NULL DEFAULT now(),
end_date TIMESTAMP,
error TEXT,
status status_enum, -- Por definir los status que va a usar
)
-- operaciones pendientes para revisar
CREATE INDEX pending_operations
ON objenious_operation(start_date)
WHERE end_date IS NULL
CREATE TABLE if not exists objenious_operation_change (
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
operation_id BIGINT,
creation_date TIMESTAMP NOT NULL DEFAULT now(),
error TEXT,
new_status status_enum,
new_request_id TEXT,
new_mass_action_id TEXT,
CONSTRAINT fk_operation_id
FOREIGN KEY(operation_id) REFERENCES objenious_operation(id)
)
CREATE INDEX fk_operation_change
ON objenious_operation_change(operation_id)

View File

@@ -1,3 +1,6 @@
/**
* Result<Error,Data>
*/
export type Result<E, D> = {
error: E | undefined,
data: D | undefined

View File

@@ -1,6 +1,6 @@
import { Pool, QueryResult, QueryResultRow } from "pg";
export class postgreClient {
export class PgClient {
private pgPool: Pool;
constructor(args: {
@@ -9,13 +9,17 @@ export class postgreClient {
this.pgPool = args.pool
}
public connect() {
return this.pgPool.connect()
}
/**
* Wrapper para ejecutar consultas con tipado fuerte.
* T es el formato de la respusta.
* @param text - La consulta SQL (ej. 'SELECT * FROM users WHERE id = $1')
* @param params - Los valores para los placeholders $1, $2, etc.
*/
public async postgreQuery<T extends QueryResultRow = any>(
public async query<T extends QueryResultRow = any>(
text: string,
params?: any[]
): Promise<QueryResult<T>> {

View File

@@ -4,5 +4,5 @@ OBJ_AUTHORIZATION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSI
OBJ_CLI_ASSERTION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ
OBJ_CLIENT_ID=savefamily_rest_ws
OBJ_KID=xNfbMiyL1ORXGP8lElhcv8nVaG3EJKye4Lc1YoN3I1E
//OBJ_BASE_URL=https://api-getway.objenious.com/ws
OBJ_BASE_URL=https://api-getway.objenious.com/ws/test
OBJ_BASE_URL=https://api-getway.objenious.com/ws
//OBJ_BASE_URL=https://api-getway.objenious.com/ws/test

View File

@@ -2,6 +2,8 @@
Interaccion y particularidades de la API de objenious, el swagger con la documentacion de Objenious se encuentra en https://api-getway.objenious.com/ws/swagger-ui/index.html **Entrar solo desde Edge, no funciona con chrome**
## Procesos testados
## 1. Auth
Para todas las peticiones hace la api de Objenoius hace falta añadir un token JWT en la cabecera de Authentication estilo "Bearer". Para ello nos proporcionan el propio token de autenticacion, que expira en 5 min, y un token de refresco que dura 30min, pero a dia de hoy se puede pedir otro token en vez de refrescar el original.

View File

@@ -71,7 +71,7 @@ export class SimUseCases {
data: true
}
} catch (error) {
console.error("Error preactivacion", error)
console.error("Error preactivacion", pauseData)
return <Result<string, boolean>>{
error: "Error preactivando la sim" + pauseData.identifier,
data: true

View File

@@ -1,4 +1,5 @@
import { Pool, QueryResult } from 'pg';
import { PgClient } from '#shared/infrastructure/PgClient'
import { env } from './env';
// Configuracion de la conexion a la BDD, deberia ser la
@@ -11,3 +12,7 @@ export const pgPool = new Pool({
password: env.POSTGRES_PASSWORD,
port: Number(env.POSTGRES_PORT) || 5432,
});
export const postgrClient = new PgClient({
pool: pgPool
})

View File

@@ -0,0 +1,34 @@
import { Result } from "#shared/domain/Result";
export type StatusEnum = 'noRequestID' | 'noMassID' | 'running' | 'finished' | 'error' | 'other';
export interface IOperationsRepository {
createOperation(data: ObjeniousOperation): Promise<Result<string, ObjeniousOperation>>
updateOperation(data: ObjeniousOperationChange): Promise<Result<string, ObjeniousOperation>>
getPendingOerations(): Promise<Result<string, ObjeniousOperation>>
}
export type ObjeniousOperation = {
id?: number;
operation: string;
retry_count?: number;
max_retry?: number;
max_date_retry?: Date | null;
iccids: string[];
request_id?: string;
mass_action_id?: string;
end_date?: Date | null;
error?: string | null;
status: StatusEnum;
}
export type ObjeniousOperationChange = {
id?: number;
operation_id: number;
creation_date: Date;
error?: string | null;
new_status: StatusEnum;
new_request_id?: string;
new_mass_action_id?: string;
}

View File

@@ -0,0 +1,78 @@
import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "#domain/operationsRepository.port";
import { Result } from "#shared/domain/Result";
import { PgClient } from "#shared/infrastructure/PgClient";
export class OperationsRepository implements IOperationsRepository {
constructor(
private readonly pgClient: PgClient
) {
}
async createOperation(data: ObjeniousOperation): Promise<Result<string, ObjeniousOperation>> {
const query = `
INSERT INTO objenious_operation (operacion, iccids, status, max_rety)
VALUES ($1, $2, $3, $4)
RETURNING *`;
const iccids = data.iccids.join(",")
const values = [data.operation, data.iccids, data.status, data.max_retry];
const { rows } = await this.pgClient.query(query, values);
return <Result<string, ObjeniousOperation>>{
data: rows[0]
}
}
async updateOperation(data: ObjeniousOperationChange): Promise<Result<string, ObjeniousOperation>> {
const client = await this.pgClient.connect();
const { new_status, error, new_request_id, new_mass_action_id, id } = data
try {
await client.query('BEGIN');
// 1. Actualizar objenious_operation
const updateOpQuery = `
UPDATE objenious_operation
SET status = $1, error = $2, request_id = $3, mass_action_id = $4, last_change_date = now(),
end_date = CASE WHEN $1 IN ('finished', 'error') THEN now() ELSE end_date END
WHERE id = $5`;
const operation = await client.query<ObjeniousOperation>(updateOpQuery, [new_status, error, new_request_id, new_mass_action_id, id]);
// 2. Nuevo registro en objenious_operation_change
const insertChangeQuery = `
INSERT INTO objenious_operation_change (operation_id, new_status, error, new_request_id, new_mass_action_id)
VALUES ($1, $2, $3, $4, $5)`;
await client.query(insertChangeQuery, [id, new_status, error, new_request_id, new_mass_action_id]);
await client.query('COMMIT');
return <Result<string, ObjeniousOperation>>{
data: operation.rows[0]
}
} catch (e) {
await client.query('ROLLBACK');
return <Result<string, ObjeniousOperation>>{
data: undefined,
error: e
}
} finally {
client.release();
}
}
async getPendingOperations(): Promise<Result<string, ObjeniousOperation[]>> {
// Aprovecha el índice 'pending_operations'
const query = `SELECT * FROM objenious_operation WHERE end_date IS NULL ORDER BY start_date ASC`;
try {
const { rows } = await this.pgClient.query<ObjeniousOperation>(query);
return {
error: undefined,
data: rows
};
} catch (e) {
return {
error: String(e),
data: undefined
}
}
}
}

View File

@@ -8,7 +8,7 @@
"config/*"
],
"#adapters/*": [
"adapters/*"
"infrastructure/*"
],
"#domain/*": [
"domain/*"
@@ -37,4 +37,4 @@
"files": [
"index.ts"
]
}
}