Modelo de activacion de sim con token automatico
This commit is contained in:
@@ -1,7 +1,62 @@
|
||||
export class HTTPClient {
|
||||
import { AxiosInstance, create as axiosCreate } from "axios"
|
||||
import { JWTToken } from "../domain/JWT"
|
||||
|
||||
export type JWTProvider<T> = {
|
||||
/** El servidor está solicitando un token nuevo o refrescando el actual*/
|
||||
isRefreshing: boolean
|
||||
authToken: JWTToken<T> | undefined
|
||||
tryRefreshToken: () => Promise<JWTToken<T>>
|
||||
getAccessToken: () => Promise<JWTToken<T>>
|
||||
}
|
||||
|
||||
export class HttpClient {
|
||||
|
||||
public client: AxiosInstance
|
||||
private jwtManager: JWTProvider<{}>
|
||||
|
||||
constructor(args: {
|
||||
baseURL: string,
|
||||
headers: Object,
|
||||
jwtManager: JWTProvider<{}> // todo: asociar el tipo de token
|
||||
}) {
|
||||
this.client = axiosCreate({
|
||||
...args
|
||||
})
|
||||
|
||||
this.jwtManager = args.jwtManager
|
||||
|
||||
|
||||
this.client.interceptors.request.use(
|
||||
async (config) => {
|
||||
// Idealmente estas condiciones no deberian de darse si mantenemos el
|
||||
// token valido de forma preventiva
|
||||
const token = this.jwtManager.authToken?.rawToken
|
||||
|
||||
if (token == undefined) throw new Error("No se ha obtenido el token para la peticion")
|
||||
|
||||
config.headers.Authorization = `Bearer ${this.jwtManager.authToken!.rawToken}`
|
||||
console.log("request completa", config.headers, config.data)
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
)
|
||||
|
||||
// No deberia usarlos de momento
|
||||
this.client.interceptors.response.use(
|
||||
(response) => {
|
||||
return response;
|
||||
},
|
||||
async (error) => {
|
||||
// TODO: Esta parte no tiene tipos, hay que asegurar el error
|
||||
const req = error.config
|
||||
console.error("[http] Error en la respuesta ", error)
|
||||
if (error.response?.status == 401) {
|
||||
this.jwtManager.getAccessToken()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
constructor() {
|
||||
// JWT?
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -13,8 +13,6 @@ export type RMQConnectionParams = {
|
||||
secure: boolean
|
||||
}
|
||||
|
||||
const RETRY_DELAY = 1000
|
||||
const PREFETCH_LIMIT = 1
|
||||
export class RabbitMQEventBus implements EventBus {
|
||||
private buildStructure?: (chan: Channel) => void
|
||||
|
||||
@@ -126,11 +124,20 @@ export class RabbitMQEventBus implements EventBus {
|
||||
}
|
||||
|
||||
protected async createConfirmChannel() {
|
||||
if (this.connection == undefined) throw new Error("Intentando crear un canal sin una conexion")
|
||||
const channel = this.connection?.createChannel({
|
||||
if (this.connection == undefined) throw new Error("[RMQ] Intentando crear un canal sin una conexion")
|
||||
const channel = this.connection.createChannel({
|
||||
json: true,
|
||||
setup: (channel: Channel) => {
|
||||
if (this.buildStructure != undefined) this.buildStructure(channel)
|
||||
// Exchanges comunes a todos
|
||||
channel.assertExchange("sim.exchange", "topic", { durable: true })
|
||||
channel.assertExchange("sim.dlx", "topic", { durable: true })
|
||||
|
||||
// Estructuras propias de cada servicio
|
||||
if (this.buildStructure != undefined) {
|
||||
this.buildStructure(channel)
|
||||
} else {
|
||||
console.warn("[i] Se ha creado un canal sin garantizar que exista la/s cola/s que se van a usar")
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -144,6 +151,5 @@ export class RabbitMQEventBus implements EventBus {
|
||||
});
|
||||
|
||||
return channel;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +77,13 @@ function addIATHeaders(authHeaders: Object) {
|
||||
* El servicio gestiona un par de tokens auth - refresh para las
|
||||
* operaciones de Objenious.
|
||||
* Se puede partir de tokens existentes.
|
||||
*
|
||||
* Debe tener un cliente HTTP propio para que no le afecten los
|
||||
* interceptores, sino puede haber bucles de refresco de token
|
||||
*/
|
||||
export class JWTService {
|
||||
// Igual no deberia mantener estado
|
||||
private authToken?: JWTToken<{}>
|
||||
public isRefreshing: boolean = false;
|
||||
public authToken: JWTToken<{}> | undefined
|
||||
private refreshToken?: JWTToken<{}>
|
||||
|
||||
constructor(args?: {
|
||||
@@ -132,6 +135,7 @@ export class JWTService {
|
||||
}
|
||||
)
|
||||
|
||||
this.isRefreshing = true;
|
||||
let res;
|
||||
try {
|
||||
res = (await req).data as TokensRequestResponse;
|
||||
@@ -142,6 +146,8 @@ export class JWTService {
|
||||
const errorString = "No se ha podido conseguir el token de acceso de OBJENIOUS"
|
||||
console.error(errorString, (e as AxiosError).response?.data)
|
||||
throw new Error(errorString)
|
||||
} finally {
|
||||
this.isRefreshing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,28 +1,42 @@
|
||||
import { EventBus } from "#shared/domain/EventBus.port";
|
||||
import { ConsumeMessage } from "amqplib";
|
||||
import { SimActivationUseCase } from "./SimActivation.usecase";
|
||||
|
||||
export class SimActivationController {
|
||||
private eventBus: EventBus;
|
||||
private activationUseCases: any;
|
||||
private useCases: {
|
||||
activation: SimActivationUseCase
|
||||
}
|
||||
|
||||
constructor(
|
||||
eventBus: EventBus
|
||||
eventBus: EventBus,
|
||||
useCases: {
|
||||
activation: SimActivationUseCase
|
||||
}
|
||||
) {
|
||||
this.eventBus = eventBus
|
||||
|
||||
this.useCases = useCases
|
||||
// No se si hay un sistema mejor
|
||||
// convertor en const () => {} para conservar el contexto??
|
||||
this.activateSim = this.activateSim.bind(this)
|
||||
}
|
||||
|
||||
public activateSim(msg: ConsumeMessage | null) {
|
||||
public async activateSim(msg: ConsumeMessage | null) {
|
||||
if (!this.validateActivationMsg(msg)) {
|
||||
throw new Error("Error consumiendo el mensaje no es valido")
|
||||
}
|
||||
console.log("mensaje procesado", String(msg?.content))
|
||||
const msgData = Buffer.from(JSON.parse(msg?.content.toString("utf-8") || "{}").data)
|
||||
console.log("Mensaje procesado", String(msgData))
|
||||
|
||||
// Caso de uso de activaciones
|
||||
//
|
||||
await this.useCases.activation.run({
|
||||
dueDate: new Date().toString(),
|
||||
identifier: {
|
||||
identifierType: "ICCID",
|
||||
identifiers: ["1234"]
|
||||
}
|
||||
})
|
||||
// TODO: comprobar el resultado de la opreacion
|
||||
this.eventBus.ack(msg!)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { HttpClient } from "#shared/infrastructure/HTTPClient"
|
||||
|
||||
// TODO: Pasar a un archivo de DTOs
|
||||
|
||||
export type ActivationData = {
|
||||
dueDate: string, // isodate
|
||||
filter?: {} // no se si hace falta
|
||||
identifier: {
|
||||
identifiers: string[]
|
||||
identifierType: "IMSI" | "MSISDN" | "REFERENCE" | "ICCID" | "IMEI"
|
||||
}
|
||||
}
|
||||
|
||||
export type ResponseError = {
|
||||
error: string,
|
||||
detail: Object[]
|
||||
}
|
||||
|
||||
export class SimActivationUseCase {
|
||||
private httpClient: HttpClient
|
||||
|
||||
constructor(args: {
|
||||
|
||||
httpClient: HttpClient
|
||||
}) {
|
||||
this.httpClient = args.httpClient
|
||||
}
|
||||
|
||||
public async run(activationData: ActivationData,) {
|
||||
const req = this.httpClient.client.post("/actions/preactivate", {
|
||||
...activationData
|
||||
})
|
||||
|
||||
try {
|
||||
const e = await req
|
||||
console.log("Activacion con exito", e.data)
|
||||
} catch (error) {
|
||||
console.error("Error activando ", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { JWTService } from "aplication/JWT.service"
|
||||
import { HttpClient } from "#shared/infrastructure/HTTPClient"
|
||||
|
||||
// TODO: mover a shared/infrastructure para usar en el resto de servicios
|
||||
|
||||
export const httpInstance = new HttpClient({
|
||||
baseURL: "https://api-getway.objenious.com/ws/",
|
||||
headers: {
|
||||
"content-type": "application/x-www-form-urlencoded"
|
||||
},
|
||||
jwtManager: new JWTService()
|
||||
})
|
||||
@@ -1,12 +1,22 @@
|
||||
|
||||
import { startRMQClient } from "#config/eventBusConfig"
|
||||
import { startRMQClient } from "#config/eventBus.config"
|
||||
import { httpInstance } from "#config/httpClient.config"
|
||||
import { SimActivationController } from "aplication/SimActivation.controller"
|
||||
import { SimActivationUseCase } from "aplication/SimActivation.usecase"
|
||||
|
||||
async function startWorker() {
|
||||
const rmqClient = await startRMQClient()
|
||||
const simActivationController = new SimActivationController(rmqClient)
|
||||
const httpClient = httpInstance
|
||||
const simActivationController = new SimActivationController(
|
||||
rmqClient,
|
||||
{
|
||||
activation: new SimActivationUseCase({
|
||||
httpClient: httpClient
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
rmqClient.consume("sim.activations", simActivationController.activateSim)
|
||||
rmqClient.consume("sim.objenious", simActivationController.activateSim)
|
||||
}
|
||||
|
||||
startWorker()
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
],
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"src/**/*.d.ts"
|
||||
"src/**/*.d.ts",
|
||||
"../shared/aplication/JWT.service.ts",
|
||||
"../shared/aplication/JWT.service.test.ts"
|
||||
],
|
||||
"files": [
|
||||
"index.ts"
|
||||
|
||||
@@ -55,6 +55,7 @@ export class SimController {
|
||||
msg: "Error general de activation"
|
||||
}
|
||||
}).send()
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +68,10 @@ export class SimController {
|
||||
|
||||
try {
|
||||
await this.simUseCases.cancelation({ iccid })
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "cancelation"
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Error cancelando la sim ", req.body)
|
||||
res.status(500).json({
|
||||
@@ -76,10 +81,6 @@ export class SimController {
|
||||
})
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "cancelation"
|
||||
})
|
||||
}
|
||||
|
||||
async pause(req: Request, res: Response) {
|
||||
@@ -92,6 +93,10 @@ export class SimController {
|
||||
|
||||
try {
|
||||
await this.simUseCases.cancelation({ iccid })
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "cancelation"
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Error pausando la sim ", req.body)
|
||||
res.status(500).json({
|
||||
@@ -100,11 +105,6 @@ export class SimController {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "cancelation"
|
||||
})
|
||||
}
|
||||
|
||||
async free(req: Request, res: Response) {
|
||||
@@ -117,6 +117,10 @@ export class SimController {
|
||||
|
||||
try {
|
||||
await this.simUseCases.cancelation({ iccid })
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "liberacion"
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Error liberando la sim ", req.body)
|
||||
res.status(500).json({
|
||||
@@ -126,10 +130,6 @@ export class SimController {
|
||||
})
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "liberacion"
|
||||
})
|
||||
}
|
||||
|
||||
async save(req: Request, res: Response) {
|
||||
@@ -142,6 +142,10 @@ export class SimController {
|
||||
|
||||
try {
|
||||
await this.simUseCases.cancelation({ iccid })
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "cancelation"
|
||||
})
|
||||
} catch (err) {
|
||||
console.error("Error activando la sim ", req.body)
|
||||
res.status(500).json({
|
||||
@@ -151,10 +155,6 @@ export class SimController {
|
||||
})
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
iccid: iccid,
|
||||
operation: "cancelation"
|
||||
})
|
||||
}
|
||||
|
||||
private validateBody(body: any, res: Response) {
|
||||
|
||||
Reference in New Issue
Block a user