From 70ef1131dfdd7b2b275b424ca600cc6c06d792ec Mon Sep 17 00:00:00 2001 From: Alvar San Martin Date: Tue, 10 Feb 2026 13:20:39 +0100 Subject: [PATCH] Refactorizacion para permitir la inyeccion de dependencias en el servicio de HTTP --- docs/sim-api/Activate.bru | 2 +- package.json | 4 +- .../aplication/JWT.service.ts | 36 ++++---- .../aplication/Sim.usecases.ts | 2 +- packages/sim-consumidor-objenious/index.ts | 2 +- .../sim-consumidor-objenious/package.json | 1 + packages/sim-entrada-eventos/package.json | 2 +- .../config/httpClient.config.ts | 2 +- packages/sim-objenious-cron/index.ts | 2 +- packages/sim-objenious-cron/package.json | 2 +- .../tasks/check_objenious_request.ts | 2 +- packages/sim-shared/domain/JWT.ts | 83 +++++++++++++++---- .../domain/operationsRepository.port.ts | 0 .../sim-shared/infrastructure/HTTPClient.ts | 9 +- .../infrastructure/OperationRepository.ts | 4 +- packages/sim-shared/package.json | 2 +- packages/sim-shared/tsconfig.json | 2 +- yarn.lock | 3 +- 18 files changed, 104 insertions(+), 56 deletions(-) rename packages/{sim-consumidor-objenious => sim-shared}/domain/operationsRepository.port.ts (100%) rename packages/{sim-consumidor-objenious => sim-shared}/infrastructure/OperationRepository.ts (98%) diff --git a/docs/sim-api/Activate.bru b/docs/sim-api/Activate.bru index 55487be..58c9cf7 100644 --- a/docs/sim-api/Activate.bru +++ b/docs/sim-api/Activate.bru @@ -11,7 +11,7 @@ post { } body:form-urlencoded { - iccid: 8933201125065160380 + iccid: 8933201125065160406 offer: SAVEFAMILY1 } diff --git a/package.json b/package.json index a87aaa6..a1f8c44 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "test": "vitest watch", "build": "yarn workspaces foreach -A --exclude sim-consumidor-nos run build && cp .env dist/ && yarn setup:runtime", - "setup:runtime": "mkdir -p dist/packages/node_modules && ln -sf ../sim-shared dist/packages/node_modules/sim-shared && ln -sf ../sim-consumidor-objenious dist/packages/node_modules/sim-consumidor-objenious && ln -sf ../sim-entrada-eventos dist/packages/node_modules/sim-entrada-eventos && ln -sf ../sim-objenious-cron dist/packages/node_modules/sim-objenious-cron", + "setup:runtime": "mkdir -p dist/packages/node_modules && ln -sf ../sim-shared dist/packages/node_modules/sim-shared && ln -sf ../sf-consumidor-objenious dist/packages/node_modules/sim-consumidor-objenious", "start": "yarn setup:runtime && yarn workspaces foreach -Apiv --exclude sim-consumidor-nos run start", "typecheck": "npx tsc --noEmit", "dev": "yarn workspaces foreach -Apiv --exclude sim-consumidor-nos run dev ", @@ -43,4 +43,4 @@ "tsx": "^4.21.0", "vitest": "^4.0.16" } -} \ No newline at end of file +} diff --git a/packages/sim-consumidor-objenious/aplication/JWT.service.ts b/packages/sim-consumidor-objenious/aplication/JWT.service.ts index fcc9bd0..038cfb8 100644 --- a/packages/sim-consumidor-objenious/aplication/JWT.service.ts +++ b/packages/sim-consumidor-objenious/aplication/JWT.service.ts @@ -1,5 +1,3 @@ -// PEM ? - /** * TODO: * Está demasiado acoplado a objenious, hay que sacar un servicio jwt general para @@ -10,7 +8,9 @@ import { env } from "#config/env/index.js"; import fs from "fs" import { - JWTToken + JWTToken, + JWTHeader, + IJWTService } from "sim-shared/domain/JWT.js" import axios, { AxiosError } from "axios"; @@ -32,15 +32,6 @@ type TokensRequestResponse = { "scope": string } -type AuthHeaders = { - content_type: string, - sub: string, - iss: string, - aud: string, - jti: string, - iat: number, - exp: number, -} const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH @@ -64,7 +55,7 @@ const DEFAULT_HEADERS = { } function addIATHeaders(authHeaders: Object) { - const headers = { + const headers = { ...authHeaders, sub: env.OBJ_CLIENT_ID, iss: env.OBJ_CLIENT_ID, @@ -76,6 +67,7 @@ function addIATHeaders(authHeaders: Object) { return headers } +export type ObjeniousTokenBody = any /** * El servicio gestiona un par de tokens auth - refresh para las @@ -85,10 +77,10 @@ function addIATHeaders(authHeaders: Object) { * Debe tener un cliente HTTP propio para que no le afecten los * interceptores, sino puede haber bucles de refresco de token */ -export class JWTService { +export class JWTService implements IJWTService { public isRefreshing: boolean = false; - public authToken: JWTToken<{}> | undefined - private refreshToken?: JWTToken<{}> + public authToken: JWTToken | undefined; + private refreshToken?: JWTToken | undefined; constructor(args?: { token?: string // si se partiese de un token existente, @@ -122,7 +114,7 @@ export class JWTService { return token } - public async getNewTokens() { + public async getNewAuthToken() { const bodyWithtoken = { ...DEFAULT_BODY, client_assertion: this.buildJwtBody() @@ -139,8 +131,8 @@ export class JWTService { let res; try { res = (await req).data as TokensRequestResponse; - this.authToken = new JWTToken(res.access_token) - this.refreshToken = new JWTToken(res.refresh_token) + this.authToken = new JWTToken(res.access_token) + this.refreshToken = new JWTToken(res.refresh_token) return this.authToken } catch (e) { const errorString = "No se ha podido conseguir el token de acceso de OBJENIOUS" @@ -163,7 +155,7 @@ export class JWTService { } // Caso 3: Ningún token es valido - await this.getNewTokens() + await this.getNewAuthToken() if (this.authToken == undefined) throw new Error("Error obteniendo tokens de auth") @@ -190,8 +182,8 @@ export class JWTService { let res; try { res = (await req).data as TokensRequestResponse; - this.authToken = new JWTToken(res.access_token) - this.refreshToken = new JWTToken(res.refresh_token) + this.authToken = new JWTToken(res.access_token) + this.refreshToken = new JWTToken(res.refresh_token) return this.authToken } catch (e) { const errorString = "No se ha podido conseguir el token de acceso de OBJENIOUS" diff --git a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts index 7822420..2842a93 100644 --- a/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts +++ b/packages/sim-consumidor-objenious/aplication/Sim.usecases.ts @@ -2,7 +2,7 @@ import { ActionData, ActivationData } from "#domain/DTOs/objeniousapi.js" import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js" import { AxiosError } from "axios" import { Result } from "sim-shared/domain/Result.js" -import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } from "#domain/operationsRepository.port.js" +import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } from "sim-shared/domain/operationsRepository.port.js" // TODO: // - Pasar a un archivo de DTOs diff --git a/packages/sim-consumidor-objenious/index.ts b/packages/sim-consumidor-objenious/index.ts index c7a31d4..d616a8a 100644 --- a/packages/sim-consumidor-objenious/index.ts +++ b/packages/sim-consumidor-objenious/index.ts @@ -1,5 +1,5 @@ -import { OperationsRepository } from "#adapters/OperationRepository.js" +import { OperationsRepository } from "sim-shared/infrastructure/OperationRepository.js" import { startRMQClient } from "#config/eventBus.config.js" import { httpInstance } from "#config/httpClient.config.js" import { pgPool } from "#config/postgreConfig.js" diff --git a/packages/sim-consumidor-objenious/package.json b/packages/sim-consumidor-objenious/package.json index 135c24e..756a2c7 100644 --- a/packages/sim-consumidor-objenious/package.json +++ b/packages/sim-consumidor-objenious/package.json @@ -68,6 +68,7 @@ "cors": "*", "dotenv": "*", "express": "*", + "sim-consumidor-objenious": "sim-consumidor-objenious:*", "sim-shared": "sim-shared:*", "typescript": "*" }, diff --git a/packages/sim-entrada-eventos/package.json b/packages/sim-entrada-eventos/package.json index f4380f0..7c7d826 100644 --- a/packages/sim-entrada-eventos/package.json +++ b/packages/sim-entrada-eventos/package.json @@ -73,4 +73,4 @@ "tsx": "*", "vitest": "*" } -} \ No newline at end of file +} diff --git a/packages/sim-objenious-cron/config/httpClient.config.ts b/packages/sim-objenious-cron/config/httpClient.config.ts index bd612a5..89fe5d1 100644 --- a/packages/sim-objenious-cron/config/httpClient.config.ts +++ b/packages/sim-objenious-cron/config/httpClient.config.ts @@ -1,6 +1,6 @@ import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js" -import { JWTService } from "sim-consumidor-objenious/aplication/JWT.service.js" import { env } from "./env/index.js" +import { JWTService } from "packages/sim-consumidor-objenious/aplication/JWT.service.js" const OBJ_BASE_URL = env.OBJ_BASE_URL diff --git a/packages/sim-objenious-cron/index.ts b/packages/sim-objenious-cron/index.ts index a46f373..a9d0e47 100644 --- a/packages/sim-objenious-cron/index.ts +++ b/packages/sim-objenious-cron/index.ts @@ -1,9 +1,9 @@ import { pgPool } from "./config/postgreConfig.js" import { PgClient } from "sim-shared/infrastructure/PgClient.js" -import { OperationsRepository } from "../sim-consumidor-objenious/infrastructure/OperationRepository.js" import { httpInstance } from "./config/httpClient.config.js" import { CheckObjeniousRequests } from "./tasks/check_objenious_request.js" +import { OperationsRepository } from "sim-shared/infrastructure/OperationRepository.js" async function startCron() { const commonSettings = { diff --git a/packages/sim-objenious-cron/package.json b/packages/sim-objenious-cron/package.json index 843c4e2..bf8da38 100644 --- a/packages/sim-objenious-cron/package.json +++ b/packages/sim-objenious-cron/package.json @@ -73,4 +73,4 @@ "tsx": "*", "vitest": "*" } -} \ No newline at end of file +} diff --git a/packages/sim-objenious-cron/tasks/check_objenious_request.ts b/packages/sim-objenious-cron/tasks/check_objenious_request.ts index ca652d5..f47897e 100644 --- a/packages/sim-objenious-cron/tasks/check_objenious_request.ts +++ b/packages/sim-objenious-cron/tasks/check_objenious_request.ts @@ -1,4 +1,4 @@ -import { IOperationsRepository, Objenious, ObjeniousOperation, ObjeniousOperationChange, StatusEnum } from "sim-consumidor-objenious/domain/operationsRepository.port.js" +import { IOperationsRepository, Objenious, ObjeniousOperation, ObjeniousOperationChange, StatusEnum } from "sim-shared/domain/operationsRepository.port.js"; import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"; export class CheckObjeniousRequests { diff --git a/packages/sim-shared/domain/JWT.ts b/packages/sim-shared/domain/JWT.ts index 72017c5..d746eba 100644 --- a/packages/sim-shared/domain/JWT.ts +++ b/packages/sim-shared/domain/JWT.ts @@ -1,11 +1,45 @@ +/** + * Herramientas para gestionar todos los trabajos con JWT, los tipos + * definidos aquí deben de ser sufcientemente generales para usarse + * en todos los proyectos. + * + * Las cabeceras de un token y la firma son standard para todos, pero + * el body puede variar con contenido nuevo. + */ + import { sign } from "node:crypto" -export type JWTHeader = { - alg: string, - typ: string, - kid: string +export interface IJWTService { + /* Obtener un token de auth sin comprobar nada */ + getNewAuthToken: () => Promise>, + /* Obtener un token valido -> sino refrescar -> sin pillar un token nuevo */ + getAccessToken: () => Promise>, + /* Intenta refrescar el token actual */ + tryRefreshToken: () => Promise>, } +export type JWTHeader = { + /* algoritmo de firma */ + alg: string, + /* tipo de token */ + typ: string, + /* key ID */ + kid?: string + /* content type */ + cty?: string + /**/ + iss?: string, + sub?: string, + aud?: string, + jti?: string, + iat?: number, + exp?: number, +} + +/* + * Define los campos basicos del payload de un token, en se + * añaden los campos espeficos de la comunicacion + * */ export type JWTPayload = { /** (Issuer) Quién emitió el token */ iss?: string; @@ -45,29 +79,33 @@ export type JWT = { } // todo pasar a la clase JWT -function signJWT(args: { - algorythm: "sha256" | string, - data: string, - privateKey: string -}) { +function signJWT(args: SignatureOptions) { const signature = sign( args.algorythm, Buffer.from(args.data), args.privateKey ) - return signature + return signature.toString("base64url") +} + +export type SignatureOptions = { + algorythm: "sha256" | string, + data: string, + privateKey: string, } export class JWTToken { - public rawToken: string private decodedPayload: JWTPayload | undefined + private signatureFunc = signJWT constructor( - token: string + token: string, + externalSignatureFunc?: (args: SignatureOptions) => string ) { this.rawToken = token this.decodedPayload = this.decodePayload() + if (externalSignatureFunc != undefined) this.signatureFunc = externalSignatureFunc } public static fromParts(args: { @@ -76,10 +114,13 @@ export class JWTToken { sigantureData?: { privateKey: string, algorythm: string - } + }, + signatureFunc?: (args: SignatureOptions) => string }) { const strHeader = JSON.stringify(args.header) const base64Header = Buffer.from(strHeader).toString("base64url") + const signatureFunc = args.signatureFunc ?? signJWT + let token = base64Header if (args.payload != undefined) { @@ -89,11 +130,11 @@ export class JWTToken { } if (args.sigantureData != undefined) { - const base64signature = signJWT({ + const base64signature = signatureFunc({ algorythm: args.sigantureData.algorythm, privateKey: args.sigantureData.privateKey, - data: token - }).toString("base64url") + data: token, + }) token += ("." + base64signature) } @@ -116,4 +157,14 @@ export class JWTToken { if (expirationDate * 1000 <= now.getTime()) return true return false } + + public isValid() { + throw new Error("No implementado") + } + + public verifySignature() { + throw new Error("No implementado") + } + } + diff --git a/packages/sim-consumidor-objenious/domain/operationsRepository.port.ts b/packages/sim-shared/domain/operationsRepository.port.ts similarity index 100% rename from packages/sim-consumidor-objenious/domain/operationsRepository.port.ts rename to packages/sim-shared/domain/operationsRepository.port.ts diff --git a/packages/sim-shared/infrastructure/HTTPClient.ts b/packages/sim-shared/infrastructure/HTTPClient.ts index b638902..00084d1 100644 --- a/packages/sim-shared/infrastructure/HTTPClient.ts +++ b/packages/sim-shared/infrastructure/HTTPClient.ts @@ -1,5 +1,7 @@ import axios, { AxiosInstance } from "axios" -import { JWTToken } from "../domain/JWT.js" +import { IJWTService, JWTToken } from "../domain/JWT.js" + +// Cambiar por IJWRGeneralService export type JWTProvider = { /** El servidor está solicitando un token nuevo o refrescando el actual*/ @@ -13,11 +15,13 @@ export class HttpClient { public client: AxiosInstance private jwtManager: JWTProvider<{}> + private jwtService: IJWTService | undefined; constructor(args: { baseURL: string, headers: Object, - jwtManager: JWTProvider<{}> // todo: asociar el tipo de token + jwtManager: JWTProvider<{}> // todo: asociar el tipo de token, + jwtService?: IJWTService }) { this.client = axios.create({ ...args @@ -25,6 +29,7 @@ export class HttpClient { this.jwtManager = args.jwtManager + if (args.jwtService != undefined) this.jwtService = args.jwtService this.client.interceptors.request.use( async (config) => { diff --git a/packages/sim-consumidor-objenious/infrastructure/OperationRepository.ts b/packages/sim-shared/infrastructure/OperationRepository.ts similarity index 98% rename from packages/sim-consumidor-objenious/infrastructure/OperationRepository.ts rename to packages/sim-shared/infrastructure/OperationRepository.ts index 7687ee3..4717756 100644 --- a/packages/sim-consumidor-objenious/infrastructure/OperationRepository.ts +++ b/packages/sim-shared/infrastructure/OperationRepository.ts @@ -1,9 +1,7 @@ -import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "#domain/operationsRepository.port.js"; +import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "sim-shared/domain/operationsRepository.port.js"; import { Result } from "sim-shared/domain/Result.js"; import { PgClient } from "sim-shared/infrastructure/PgClient.js"; - - export class OperationsRepository implements IOperationsRepository { constructor( diff --git a/packages/sim-shared/package.json b/packages/sim-shared/package.json index 300e8c0..ab5a0d0 100644 --- a/packages/sim-shared/package.json +++ b/packages/sim-shared/package.json @@ -57,4 +57,4 @@ "tsx": "*", "vitest": "*" } -} \ No newline at end of file +} diff --git a/packages/sim-shared/tsconfig.json b/packages/sim-shared/tsconfig.json index c027227..9c9d7f1 100644 --- a/packages/sim-shared/tsconfig.json +++ b/packages/sim-shared/tsconfig.json @@ -9,6 +9,6 @@ ], "include": [ "**/*.ts", - "../../packages/sim-shared/**/*.ts" + "../../packages/sim-shared/**/*.ts", ] } diff --git a/yarn.lock b/yarn.lock index a41e490..e77082f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2769,7 +2769,7 @@ __metadata: languageName: unknown linkType: soft -"sim-consumidor-objenious@workspace:packages/sim-consumidor-objenious": +"sim-consumidor-objenious@sim-consumidor-objenious:*, sim-consumidor-objenious@workspace:packages/sim-consumidor-objenious": version: 0.0.0-use.local resolution: "sim-consumidor-objenious@workspace:packages/sim-consumidor-objenious" dependencies: @@ -2784,6 +2784,7 @@ __metadata: dotenv: "npm:*" express: "npm:*" prettier: "npm:*" + sim-consumidor-objenious: "sim-consumidor-objenious:*" sim-shared: "sim-shared:*" supertest: "npm:*" tsc-alias: "npm:^1.8.16"