Repositorio para llevar el registro de operaciones a objenious
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
42
deployment/database/01-objenious.sql
Normal file
42
deployment/database/01-objenious.sql
Normal 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)
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* Result<Error,Data>
|
||||
*/
|
||||
export type Result<E, D> = {
|
||||
error: E | undefined,
|
||||
data: D | undefined
|
||||
|
||||
@@ -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>> {
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"config/*"
|
||||
],
|
||||
"#adapters/*": [
|
||||
"adapters/*"
|
||||
"infrastructure/*"
|
||||
],
|
||||
"#domain/*": [
|
||||
"domain/*"
|
||||
@@ -37,4 +37,4 @@
|
||||
"files": [
|
||||
"index.ts"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user