diff --git a/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql new file mode 100644 index 0000000..44bb3dd --- /dev/null +++ b/deployment/database/migrations/1.2.0_Cola-pausa-cancelacion.sql @@ -0,0 +1,24 @@ +/** +* Para la tarea WEBINT-328-Pausas-cacelaciones. +* Almacena las pausas/cancelaciones que no se han podido hacer porque la linea esta en +* "Test" +*/ + +CREATE TABLE IF NOT EXISTS pause_cancel_tasks ( + id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + iccid TEXT NOT NULL, + + last_checked TIMESTAMPTZ, -- Última vez que se ha comprobado que no esté en test + activation_date TIMESTAMPTZ, -- Fecha de activacion para comprobar si ha pasdo un mes + next_check TIMESTAMPTZ, -- Si se ha comprobado se asignará la siguiente fecha de revision + + completed_date TIMESTAMPTZ, -- Cuando se ha completado, para bien o mal. + error TEXT +); + +-- Indice de las tareas que no han terminado +CREATE INDEX idx_pause_cancel_tasks_pending +ON pause_cancel_tasks (next_check) +WHERE completed_date IS NULL; + + diff --git a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts index 7c60673..b56db69 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.controller.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.controller.ts @@ -3,7 +3,6 @@ import { ConsumeMessage } from "amqplib"; import { SimUseCases } from "./Sim.usecases.js"; import { SimEvents } from "sim-shared/domain/SimEvents.js"; import { Result } from "sim-shared/domain/Result.js"; -import { env } from "#config/env/index.js"; /** * La clase usa generadores de funciones para mantener el contexto @@ -157,6 +156,9 @@ export class SimController { } } + /** + * Lo mismo que pause + */ public suspend() { return async (msg: ConsumeMessage) => { let msgData; diff --git a/packages/sim-consumidor-objenious/config/httpClient.config.ts b/packages/sim-consumidor-objenious/config/httpClient.config.ts index 6d37725..427b6e8 100644 --- a/packages/sim-consumidor-objenious/config/httpClient.config.ts +++ b/packages/sim-consumidor-objenious/config/httpClient.config.ts @@ -1,6 +1,6 @@ import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js" -import { JWTService } from "../aplication/JWT.service.js" import { env } from "./env/index.js" +import { jwtService } from "./jwtService.config.js" const OBJ_BASE_URL = env.OBJ_BASE_URL @@ -9,5 +9,5 @@ export const httpInstance = new HttpClient({ headers: { "content-type": " application/json; charset=utf-8" }, - jwtManager: new JWTService() + jwtManager: jwtService }) diff --git a/packages/sim-consumidor-objenious/config/jwtService.config.ts b/packages/sim-consumidor-objenious/config/jwtService.config.ts new file mode 100644 index 0000000..3d9bf78 --- /dev/null +++ b/packages/sim-consumidor-objenious/config/jwtService.config.ts @@ -0,0 +1,59 @@ +import { GrantAccessRequestBody, JWTService } from "sim-shared/aplication/JWT.service.js" +import { env } from "./env/index.js" +import { JWTHeader } from "sim-shared/domain/JWT.js" + + +const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH + +const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" +const REFRESH_TOKEN_URL = GET_TOKEN_URL + +const DEFAULT_BODY: GrantAccessRequestBody = { + grant_type: "client_credentials", + client_id: env.OBJ_CLIENT_ID, + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: env.OBJ_CLI_ASSERTION +} + + +const DEFAULT_HEADERS = { + "content-type": "application/x-www-form-urlencoded" +} + +const DEFAULT_HEADERS_JWT = { + alg: "RS256", + typ: "JWT", + kid: env.OBJ_KID, +} + +const DEFAULT_DATA_JWT = { + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: "https://idp.docapost.io/auth/realms/GETWAY", + jti: Date.now().toString(), + +} + +function addIATHeaders(authHeaders: Object) { + const headers = { + ...authHeaders, + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: GET_TOKEN_URL, + jti: Date.now().toString(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 5 * 60, + } + return headers +} + +export const jwtService = new JWTService({ + transformJWTHeaders: addIATHeaders, + defaultHeaders: DEFAULT_HEADERS, + defaultBody: DEFAULT_BODY, + defaultJWTHeaders: DEFAULT_HEADERS_JWT, + defaultJWTPayload: DEFAULT_DATA_JWT, + privateKeyPath: PRIVATE_KEY_PATH, + tokenUrl: GET_TOKEN_URL, + refreshTokenUrl: REFRESH_TOKEN_URL +}) diff --git a/packages/sim-objenious-cron/config/httpClient.config.ts b/packages/sim-objenious-cron/config/httpClient.config.ts index 89fe5d1..c526054 100644 --- a/packages/sim-objenious-cron/config/httpClient.config.ts +++ b/packages/sim-objenious-cron/config/httpClient.config.ts @@ -1,6 +1,7 @@ import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js" import { env } from "./env/index.js" -import { JWTService } from "packages/sim-consumidor-objenious/aplication/JWT.service.js" +import { jwtService } from "./jwtService.config.js" + const OBJ_BASE_URL = env.OBJ_BASE_URL @@ -9,5 +10,5 @@ export const httpInstance = new HttpClient({ headers: { "content-type": " application/json; charset=utf-8" }, - jwtManager: new JWTService() + jwtManager: jwtService }) diff --git a/packages/sim-objenious-cron/config/jwtService.config.ts b/packages/sim-objenious-cron/config/jwtService.config.ts new file mode 100644 index 0000000..3d9bf78 --- /dev/null +++ b/packages/sim-objenious-cron/config/jwtService.config.ts @@ -0,0 +1,59 @@ +import { GrantAccessRequestBody, JWTService } from "sim-shared/aplication/JWT.service.js" +import { env } from "./env/index.js" +import { JWTHeader } from "sim-shared/domain/JWT.js" + + +const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH + +const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" +const REFRESH_TOKEN_URL = GET_TOKEN_URL + +const DEFAULT_BODY: GrantAccessRequestBody = { + grant_type: "client_credentials", + client_id: env.OBJ_CLIENT_ID, + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: env.OBJ_CLI_ASSERTION +} + + +const DEFAULT_HEADERS = { + "content-type": "application/x-www-form-urlencoded" +} + +const DEFAULT_HEADERS_JWT = { + alg: "RS256", + typ: "JWT", + kid: env.OBJ_KID, +} + +const DEFAULT_DATA_JWT = { + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: "https://idp.docapost.io/auth/realms/GETWAY", + jti: Date.now().toString(), + +} + +function addIATHeaders(authHeaders: Object) { + const headers = { + ...authHeaders, + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: GET_TOKEN_URL, + jti: Date.now().toString(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 5 * 60, + } + return headers +} + +export const jwtService = new JWTService({ + transformJWTHeaders: addIATHeaders, + defaultHeaders: DEFAULT_HEADERS, + defaultBody: DEFAULT_BODY, + defaultJWTHeaders: DEFAULT_HEADERS_JWT, + defaultJWTPayload: DEFAULT_DATA_JWT, + privateKeyPath: PRIVATE_KEY_PATH, + tokenUrl: GET_TOKEN_URL, + refreshTokenUrl: REFRESH_TOKEN_URL +}) diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index 84e8cd7..9153d39 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -21,7 +21,10 @@ async function startCron() { console.log("[i] Comprobando conexion con la BDD ") await pgClient.checkDatabaseConnection() - const operationRepository = new ObjeniousOperationsRepository(pgClient) + const operationRepository = new ObjeniousOperationsRepository( + httpClient, + pgClient, + ) const orderRepository = new OrderRepository(pgClient) const objeniousLineRepository = new ObjeniousLinesRepository(postgresClientIntranet) @@ -31,7 +34,15 @@ async function startCron() { httpClient, ) - const volcadoLineasTask = new TaskVolcadoLineas(httpClient, objeniousLineRepository) + const objeniosRepo = new ObjeniousOperationsRepository( + httpClient, + pgClient + ) + + const volcadoLineasTask = new TaskVolcadoLineas( + objeniousLineRepository, + objeniosRepo + ) const PERIODO_PETICIONES = 10 * 60 * 1000 const interval = setInterval(async () => { diff --git a/packages/sim-objenious-cron/tasks/volcado_lineas.ts b/packages/sim-objenious-cron/tasks/volcado_lineas.ts index e8cbd8a..bea139a 100644 --- a/packages/sim-objenious-cron/tasks/volcado_lineas.ts +++ b/packages/sim-objenious-cron/tasks/volcado_lineas.ts @@ -1,94 +1,14 @@ -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 { lineToCreateLineDto, ObjeniousLine } from "sim-shared/domain/objeniousLine.js"; import { ObjeniousLinesRepository } from "../infranstructure/ObjeniousLinesRepository.js"; -import { AxiosResponse } from "axios"; -import { constants } from "node:buffer"; - -const MAX_PAGE_SIZE = 100 +import { ObjeniousOperationsRepository } from "packages/sim-shared/infrastructure/ObjeniousOperationRepository.js"; export class TaskVolcadoLineas { constructor( - private readonly httpClient: HttpClient, private readonly linesRepository: ObjeniousLinesRepository, + private readonly objeniousRepository: ObjeniousOperationsRepository ) { } - /** - * Mover al repo - */ - private async * getLinesByStatus(args?: { - pageSize?: number, - pageNumber?: number, - status?: string - }): AsyncGenerator, Result, 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 = {} - - const loadNextLine = async (page: number): Promise> => { - 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>(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) @@ -107,7 +27,9 @@ export class TaskVolcadoLineas { 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() + const linesIterator = this.objeniousRepository.getLinesByStatusAPI({ + pageSize: 100 + }) let lines = await linesIterator.next() if (lines.value.error != undefined || lines.value.data == undefined) { diff --git a/packages/sim-consumidor-objenious/aplication/JWT.service.test.ts b/packages/sim-shared/aplication/JWT.service.test.ts similarity index 61% rename from packages/sim-consumidor-objenious/aplication/JWT.service.test.ts rename to packages/sim-shared/aplication/JWT.service.test.ts index d7f2f43..1d37d7e 100644 --- a/packages/sim-consumidor-objenious/aplication/JWT.service.test.ts +++ b/packages/sim-shared/aplication/JWT.service.test.ts @@ -1,16 +1,16 @@ import { test, describe } from "vitest" -import { JWTService } from "./JWT.service.js" +import { jwtService } from "../config/jwtService.config.js" describe("Tokens Objenious", () => { - const jwtService = new JWTService() + const jwt = jwtService test("Solicicitud normal de auth", async () => { - const token = await jwtService.getAccessToken() + const token = await jwt.getAccessToken() console.log("acceso objenious", token) }), test("Solicicitud de refresh de auth", async () => { - const token = await jwtService.tryRefreshToken() + const token = await jwt.tryRefreshToken() console.log("acceso refresh objenious", token) }) }) diff --git a/packages/sim-consumidor-objenious/aplication/JWT.service.ts b/packages/sim-shared/aplication/JWT.service.ts similarity index 66% rename from packages/sim-consumidor-objenious/aplication/JWT.service.ts rename to packages/sim-shared/aplication/JWT.service.ts index 038cfb8..9ef739e 100644 --- a/packages/sim-consumidor-objenious/aplication/JWT.service.ts +++ b/packages/sim-shared/aplication/JWT.service.ts @@ -4,24 +4,24 @@ * el cliente HTTP */ -import { env } from "#config/env/index.js"; import fs from "fs" import { JWTToken, JWTHeader, - IJWTService + IJWTService, + JWTPayload } from "sim-shared/domain/JWT.js" import axios, { AxiosError } from "axios"; -type GrantAccessRequestBody = { +export type GrantAccessRequestBody = { grant_type: string, client_id: string, client_assertion_type: string, client_assertion: string } -type TokensRequestResponse = { +export type TokensRequestResponse = { "access_token": string, "expires_in": number, "refresh_token": string @@ -32,41 +32,6 @@ type TokensRequestResponse = { "scope": string } - -const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH - -const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" -const REFRESH_TOKEN_URL = GET_TOKEN_URL - -const DEFAULT_BODY: GrantAccessRequestBody = { - grant_type: "client_credentials", - client_id: env.OBJ_CLIENT_ID, - client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", - client_assertion: env.OBJ_CLI_ASSERTION -} - -const REFRESH_BODY = { - ...DEFAULT_BODY, - grant_type: "refresh_token", -} - -const DEFAULT_HEADERS = { - "content-type": "application/x-www-form-urlencoded" -} - -function addIATHeaders(authHeaders: Object) { - const headers = { - ...authHeaders, - sub: env.OBJ_CLIENT_ID, - iss: env.OBJ_CLIENT_ID, - aud: GET_TOKEN_URL, - jti: Date.now().toString(), - iat: Math.floor(Date.now() / 1000), - exp: Math.floor(Date.now() / 1000) + 5 * 60, - } - return headers -} - export type ObjeniousTokenBody = any /** @@ -82,27 +47,54 @@ export class JWTService implements IJWTService { public authToken: JWTToken | undefined; private refreshToken?: JWTToken | undefined; - constructor(args?: { + // http + private transformHeaders?: (_: Object) => JWTHeader; + private defaultHttpHeaders: Record; + private defaultBody: Record; + + // jwt + private defaultJWTHeaders: JWTHeader; + private defaultJWTPayload: JWTPayload; + private privateKeyPath: string; + private tokenUrl: string; + private refreshTokenUrl: string; + + + constructor(args: { token?: string // si se partiese de un token existente, - refreshToken?: string + refreshToken?: string, + transformJWTHeaders?: (_: Object) => JWTHeader, + defaultHeaders: Record, + defaultBody: Record, + defaultJWTHeaders: JWTHeader, + defaultJWTPayload: JWTPayload, + privateKeyPath: string, + tokenUrl: string, + refreshTokenUrl: string }) { if (args?.token != undefined) this.authToken = new JWTToken(args.token) if (args?.refreshToken != undefined) this.refreshToken = new JWTToken(args.refreshToken) + if (args?.transformJWTHeaders != undefined) this.transformHeaders = args.transformJWTHeaders + + this.defaultHttpHeaders = args.defaultHeaders; + this.defaultBody = args.defaultBody; + + this.defaultJWTHeaders = args.defaultJWTHeaders; + this.defaultJWTPayload = args.defaultJWTPayload; + this.privateKeyPath = args.privateKeyPath; + + this.tokenUrl = args.tokenUrl; + this.refreshTokenUrl = args.refreshTokenUrl; } private buildJwtBody() { - const jwtHeaders = { - alg: "RS256", - typ: "JWT", - kid: env.OBJ_KID - } - const jwtData = addIATHeaders({ - sub: env.OBJ_CLIENT_ID, - iss: env.OBJ_CLIENT_ID, - aud: "https://idp.docapost.io/auth/realms/GETWAY", - jti: Date.now().toString(), - }) - const key = fs.readFileSync(PRIVATE_KEY_PATH, "utf8") + const jwtHeaders = this.defaultJWTHeaders + + const jwtData = (this.transformHeaders) ? + this.transformHeaders(this.defaultJWTPayload) : + this.defaultJWTPayload; + + const key = fs.readFileSync(this.privateKeyPath, "utf8") const token = JWTToken.fromParts({ header: jwtHeaders, payload: jwtData, @@ -116,14 +108,16 @@ export class JWTService implements IJWTService { public async getNewAuthToken() { const bodyWithtoken = { - ...DEFAULT_BODY, + ...this.defaultBody, client_assertion: this.buildJwtBody() } - const req = axios.post(GET_TOKEN_URL, + const headers = (this.transformHeaders) ? this.transformHeaders(this.defaultHttpHeaders) : this.defaultHttpHeaders; + + const req = axios.post(this.tokenUrl, bodyWithtoken, { - headers: addIATHeaders(DEFAULT_HEADERS) + headers: headers } ) @@ -166,16 +160,21 @@ export class JWTService implements IJWTService { if (this.refreshToken == undefined) throw new Error("El refreshToken no está definido") if (this.refreshToken.isExpired()) throw new Error("El refreshToken ha expirado") + const refreshBody = { + ...this.defaultBody, + grant_type: "refresh_token", + } + const body = { - ...REFRESH_BODY, + ...refreshBody, client_assertion: this.buildJwtBody(), refresh_token: this.refreshToken.rawToken } - const req = axios.post(REFRESH_TOKEN_URL, + const req = axios.post(this.refreshTokenUrl, body, { - headers: DEFAULT_HEADERS + headers: this.defaultHttpHeaders } ) diff --git a/packages/sim-shared/config/config.test.ts b/packages/sim-shared/config/config.test.ts index 22fb083..b8e2c7c 100644 --- a/packages/sim-shared/config/config.test.ts +++ b/packages/sim-shared/config/config.test.ts @@ -7,9 +7,12 @@ import { env, loadEnvFile } from "node:process"; import { Pool } from "pg"; import { PgClient } from "../infrastructure/PgClient.js"; +import { HttpClient } from "../infrastructure/HTTPClient.js"; +import { jwtService } from "./jwtService.config.js"; console.warn("[i!] Se está corriendo codigo de test") loadEnvFile("../../.env") // Global +loadEnvFile("./test.env") // Local // se hace una por servicio. export const pgPool = new Pool({ @@ -24,4 +27,14 @@ export const postgresClient = new PgClient({ pool: pgPool }) +const OBJ_BASE_URL = "https://api-getway.objenious.com/ws" +export const httpObjClient = new HttpClient({ + baseURL: OBJ_BASE_URL, + headers: { + "content-type": " application/json; charset=utf-8" + }, + jwtManager: jwtService +}) + + console.warn(`[T] TEST DB : ${env.POSTGRES_DATABASE}@${env.POSTGRES_HOST}`) diff --git a/packages/sim-shared/config/jwtService.config.ts b/packages/sim-shared/config/jwtService.config.ts new file mode 100644 index 0000000..c2500b8 --- /dev/null +++ b/packages/sim-shared/config/jwtService.config.ts @@ -0,0 +1,67 @@ +import assert from "assert" +import { env, loadEnvFile } from "process" +import { GrantAccessRequestBody, JWTService } from "sim-shared/aplication/JWT.service.js" +import { JWTHeader } from "sim-shared/domain/JWT.js" + +loadEnvFile("../../.env") // Global +loadEnvFile("./test.env") // Local + +assert(env.OBJ_CLIENT_ID != undefined) +assert(env.OBJ_CLI_ASSERTION != undefined) +assert(env.OBJ_PEM_PATH != undefined) + +const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH + +const GET_TOKEN_URL = "https://idp.docapost.io/auth/realms/GETWAY/protocol/openid-connect/token" +const REFRESH_TOKEN_URL = GET_TOKEN_URL + + +const DEFAULT_BODY: GrantAccessRequestBody = { + grant_type: "client_credentials", + client_id: env.OBJ_CLIENT_ID, + client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", + client_assertion: env.OBJ_CLI_ASSERTION +} + + +const DEFAULT_HEADERS = { + "content-type": "application/x-www-form-urlencoded" +} + +const DEFAULT_HEADERS_JWT = { + alg: "RS256", + typ: "JWT", + kid: env.OBJ_KID, +} + +const DEFAULT_DATA_JWT = { + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: "https://idp.docapost.io/auth/realms/GETWAY", + jti: Date.now().toString(), + +} + +function addIATHeaders(authHeaders: Object) { + const headers = { + ...authHeaders, + sub: env.OBJ_CLIENT_ID, + iss: env.OBJ_CLIENT_ID, + aud: GET_TOKEN_URL, + jti: Date.now().toString(), + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 5 * 60, + } + return headers +} + +export const jwtService = new JWTService({ + transformJWTHeaders: addIATHeaders, + defaultHeaders: DEFAULT_HEADERS, + defaultBody: DEFAULT_BODY, + defaultJWTHeaders: DEFAULT_HEADERS_JWT, + defaultJWTPayload: DEFAULT_DATA_JWT, + privateKeyPath: PRIVATE_KEY_PATH, + tokenUrl: GET_TOKEN_URL, + refreshTokenUrl: REFRESH_TOKEN_URL +}) diff --git a/packages/sim-shared/domain/Result.ts b/packages/sim-shared/domain/Result.ts index a8bda25..0381424 100644 --- a/packages/sim-shared/domain/Result.ts +++ b/packages/sim-shared/domain/Result.ts @@ -14,7 +14,7 @@ export type Failure = { */ export type Result = Failure | Success -export async function tryCatch(func: Promise): Promise> { +export async function tryCatch(func: Promise): Promise> { try { const res = await func; return { @@ -22,9 +22,8 @@ export async function tryCatch(func: Promise): Promise { + const repository = new ObjeniousOperationsRepository( + httpObjClient, + postgresClient + ) + + it("Read /lines with multiple iccids", () => { + + }) +}) diff --git a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts index 134da3f..a48523a 100644 --- a/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts +++ b/packages/sim-shared/infrastructure/ObjeniousOperationRepository.ts @@ -1,14 +1,131 @@ import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "sim-shared/domain/operationsRepository.port.js"; -import { Result } from "sim-shared/domain/Result.js"; +import { Result, tryCatch } from "sim-shared/domain/Result.js"; import { PgClient } from "sim-shared/infrastructure/PgClient.js"; +import { ObjeniousLine, ObjeniousLineResponse } from "../domain/objeniousLine.js"; +import { HttpClient } from "./HTTPClient.js"; +import assert from "node:assert"; +import { AxiosResponse } from "axios"; export class ObjeniousOperationsRepository implements IOperationsRepository { constructor( + private http: HttpClient, private readonly pgClient: PgClient ) { } + /** + * Consulta el estado de una o mas lineas directamente a la API de Objenious + */ + public async getLinesAPI( + identifierType: "ICCID" | "IMSI" | "IMEI" | "MSISDN" | "REFERENCE", + identifiers: string[] + ): Promise> { + if (identifiers.length == 0) { + return { + data: [] + } + } + + // Comprobar < MAX_PAGE_SIZE (Poco probable) + + const path = "/lines" + const params = { + "identifier.identifierType": identifierType, + "identifier.identifiers": identifiers.toString() + } + + const req = this.http.client.get(path, { + params: params + }) + + const res = await tryCatch(req) + + if (res.error != undefined) { + return { + error: res.error?.message + } + } + + const lines = res.data.data + + return { + data: lines + } + } + + + private MAX_PAGE_SIZE = 1000 + public async * getLinesByStatusAPI(args?: { + pageSize?: number, + pageNumber?: number, + status?: string + }): AsyncGenerator, Result, any> { + + const path = "/lines" + const pageSize = args?.pageSize ?? this.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 = {} + + const loadNextLine = async (page: number): Promise> => { + if (args?.status != undefined) params["simStatus"] = args.status + params["pageSize"] = pageSize + params["pageNumber"] = page + console.log(`[i] Cargando pagina ${currentPage} de ${totalPages ?? "(desc)"}`) + const nextPage = await tryCatch>(this.http.client.get(path, { + params: params + })) + + if (nextPage.error != undefined) { + console.error(nextPage.error) + return { + error: nextPage.error.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: [] + } + } async createOperation(data: ObjeniousOperation): Promise> { const query = ` INSERT INTO objenious_operation (operation, iccids, status, max_retry, request_id) diff --git a/packages/sim-shared/infrastructure/OrderRepository.test.ts b/packages/sim-shared/infrastructure/OrderRepository.test.ts index 8b33947..4636fe3 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.test.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.test.ts @@ -27,7 +27,7 @@ describe("Test OrderRepository", {}, (ctx) => { before(async () => { // Order1 const result1 = await orderRepo.createOrder(order1) - assert(result1.data != undefined) + assert.ok(result1.data != undefined, result1.error as string) testIds.push(result1.data.id) // Order2 -> Para el test de crearOrder diff --git a/packages/sim-shared/infrastructure/OrderRepository.ts b/packages/sim-shared/infrastructure/OrderRepository.ts index 15d18f8..b2e965a 100644 --- a/packages/sim-shared/infrastructure/OrderRepository.ts +++ b/packages/sim-shared/infrastructure/OrderRepository.ts @@ -3,10 +3,9 @@ */ import { PoolClient, QueryResult, QueryResultRow } from "pg"; import { CreateOrderDTO, ErrorOrderDTO, FinishOrderDTO, OrderTracking, UpdateOrderDTO } from "../domain/Order.js"; -import { Result } from "../domain/Result.js"; +import { Result, tryCatch } from "../domain/Result.js"; import { PgClient } from "./PgClient.js"; import assert from "node:assert"; -import { error } from "node:console"; /** * Agrupa todas las operaciones de *Order*. @@ -19,9 +18,8 @@ import { error } from "node:console"; */ export class OrderRepository { constructor( - private readonly pgClient: PgClient + private readonly pgClient: PgClient, ) { - } /** @@ -57,6 +55,8 @@ export class OrderRepository { } } + + /** * El tipo representa el contenido del mensaje de los order */ diff --git a/packages/sim-shared/test.env b/packages/sim-shared/test.env new file mode 100644 index 0000000..1e5983d --- /dev/null +++ b/packages/sim-shared/test.env @@ -0,0 +1,14 @@ +## ENV PARA DATOS DE TEST - shared nunca se lanza en produccion + +# claves de Objenious +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 +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 + +NOTIFICATION_URL="https://sf-sim-activation.savefamilygps.net/send-activation-mail" +# NOTIFICATION_URL="localhost" +SIM_ACTIVATION_API_KEY=9e48c4ac-1ab0-4397-b3f3-6c239200dfe6