Inicio port NOS

This commit is contained in:
2026-04-16 17:46:32 +02:00
parent 964ea6add9
commit fdbb81ba64
10 changed files with 241 additions and 57 deletions

View File

@@ -1,3 +1,6 @@
NOS_BASE_URL=localhost
ENVIORMENT=development
NOS_ACCESS_TOKEN=2YGhecTr4+uKbVKxaqBlk2edsrHA2OQY
NOS_BASE_URL=https://nosconnectcenter-api.iot-x.com

View File

@@ -1,65 +1,34 @@
import { EventBus } from "sim-shared/domain/EventBus.port.js";
import { ConsumeMessage } from "amqplib";
import { SimNosUsecases } from "./SimNOS.usecases";
export class SimNosController {
private eventBus: EventBus;
private activationUseCases: any;
private routes = new Map<string, () => void>([
["activate", async () => { console.log("caso de uso activate") }],
["pause", async () => { console.log("caso de uso pause") }],
["cancel", async () => { console.log("caso de uso cancel") }],
])
constructor(
eventBus: EventBus
uscases: SimNosUsecases
) {
this.eventBus = eventBus
// No se si hay un sistema mejor
// convertor en const () => {} para conservar el contexto??
this.recibeMsg = this.recibeMsg.bind(this)
}
public async recibeMsg(msg: ConsumeMessage | null) {
if (!this.validateActivationMsg(msg)) {
throw new Error("Error consumiendo el mensaje no es valido")
}
msg = msg!
const msgParsed = JSON.parse(String(msg.content))
const msgKey = msg.fields.routingKey.split(".")
const accion = msgKey[2]
if (accion == undefined) {
console.error("La routingKey es incorrecta: " + accion)
this.eventBus.nack(msg)
return;
}
if (this.routes.get(accion) == undefined) {
console.error("No hay una ruta definida para la accion")
this.eventBus.nack(msg)
return;
}
try {
this.routes.get(accion)!()
} catch (err) {
console.log("Error procesando el mensaje")
this.eventBus.nack(msg)
} finally {
this.eventBus.ack(msg)
public activate() {
return async (msg: ConsumeMessage) => {
console.log("Evento activate ", msg)
}
}
/**
* TODO:
* - Loguear motivos de la no validacion
*/
private validateActivationMsg(msg: ConsumeMessage | null) {
if (msg == undefined) return false;
return true;
public suspend() {
return async (msg: ConsumeMessage) => {
console.log("Evento suspend ", msg)
}
}
public terminate() {
return async (msg: ConsumeMessage) => {
console.log("Evento termiante ", msg)
}
}
public preActivate() {
return async (msg: ConsumeMessage) => {
console.log("Evento preActivate ", msg)
}
}
}

View File

@@ -0,0 +1,76 @@
/**
* Dirige cada mensaje dependiendo de el tipo de acción que contenga
* Podría hacerse con varias colas, pero así se controla mejor que
* las operaciones se hagan de 1 en 1.
*/
import { ConsumeMessage } from "amqplib";
import { SimNosController } from "./SimNOS.controller.js";
import { EventBus } from "sim-shared/domain/EventBus.port.js";
export class SimNosRouter {
private readonly routes: Map<string, undefined | ((m: ConsumeMessage) => Promise<any>)>;
constructor(
private readonly simController: SimNosController,
private readonly eventBus: EventBus
) {
this.routes = new Map([
["select", undefined],
["activate", this.simController.activate()],
["pause", this.simController.suspend()],
["cancel", this.simController.terminate()],
//["reactivate", this.simController.reActivate()],
["preActivate", this.simController.preActivate()]
]);
}
/**
* Enruta el mensaje a la acción correspondiente basándose en la routing key
* TODO: No estoy seguro que deba meter el nack aqui
* - De moemento el ack-nack se gestiona en los controller, por si acaso hay casos
* limite en
*/
public route = async (msg: ConsumeMessage | null): Promise<void> => {
if (!msg) {
console.error("[Router] Mensaje vacío");
return;
}
const action = this.extractAction(msg);
if (!action) {
console.error("[Router] La routing key no tiene una acción definida", msg.fields.routingKey);
this.eventBus.nack(msg)
return;
}
const handler = this.routes.get(action);
if (!handler) {
console.error(`[Router] La acción '${action}' no tiene un controlador asociado`);
this.eventBus.nack(msg)
return;
}
try {
console.log("[Router] Ejecutando operación:", action);
// El controlador devuelve una función (thunk) que debe ser ejecutada
const executeParams = await handler(msg);
if (typeof executeParams === "function") {
await executeParams();
}
} catch (error) {
console.error(`[Router] Error al ejecutar la operación '${action}':`, error);
this.eventBus.nack(msg)
}
};
private extractAction(msg: ConsumeMessage): string | undefined {
// Se asume que la acción está en la tercera posición: domain.compañia.accion
return msg.fields.routingKey.split(".")[2];
}
}

View File

@@ -0,0 +1,35 @@
/**
* Documentación de referencia:
* https://pelion-help.iot-x.com/nos/en-US/Content/API/APIReference/API%20Reference.htm?tocpath=_____7
*
* TODO:
* - Control de errores más preciso
*
*/
import { NosHttpClient } from "infrastructure/NosHttpClient";
export class SimNosUsecases {
constructor(
private httpClient: NosHttpClient
) {
}
public activate() {
const PATH = '/provisioning'
const PRODUCT_ID = 1330 // No se que es, preguntar a Ivan
return async (args: { iccid: string }) => {
const data = {
productSetId: PRODUCT_ID
}
try {
const res = await this.httpClient.client.post(PATH, data)
} catch (e) {
console.error(e)
}
}
}
}

View File

@@ -31,6 +31,7 @@ export const env = {
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
// ESPECIFICO NOS
NOS_BASE_URL: String(process.env.NOS_BASE_URL)
NOS_BASE_URL: String(process.env.NOS_BASE_URL),
NOS_ACCESS_TOKEN: String(process.env.NOS_ACCESS_TOKEN)
};

View File

@@ -1,6 +1,6 @@
import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js"
import { Channel } from "amqp-connection-manager"
import { env } from "./env/index.js"
import { env } from "./env/env.js"
const rmqUser = env.RABBITMQ_USER
const rmqPass = env.RABBITMQ_PASSWORD

View File

@@ -0,0 +1,48 @@
export namespace NosApi {
export type ActivateResponseOK = {
/**
The unique physical subscriber identifier:
Cellular - the ICCID
Non - IP - the EUI
Satellite - the IMEI
*/
physicalId: string,
/**
example: 447000000001
The unique network subscriber identifier:
Cellular subscriber - the MSISDN
Non - IP subscriber - the Device EUI
Satellite subscriber - the Subscription ID
*/
subscriberId: string,
/**
example: 9999
If the subscriber uses Circuit Switched Data(CSD), this field displays its data number.If the subscriber does not use CSD, this field is null.
*/
dataNumber: string
/**
example: 172.0.0.1
The subscriber IP address.
*/
ip: string
/**
example: 234150000000001
The subscriber IMSI.
*/
imsi: string
}
export type ActivateResponseError = {
error: {
children: string,
code: string,
messafe: string
}
}
}

View File

@@ -1,14 +1,37 @@
import { startRMQClient } from "#config/eventBus.config.js"
import { SimNosRouter } from "aplication/SimNOS.router.js"
import { SimNosController } from "./aplication/SimNOS.controller.js"
import { SimNosUsecases } from "aplication/SimNOS.usecases.js"
import { NosHttpClient } from "infrastructure/NosHttpClient.js"
import { env } from "#config/env/env.js"
const RMQ_QUEUE = "sim.nos"
const NOS_BASE_URL = env.NOS_BASE_URL
async function startWorker() {
// Instancia de dependencias
const rmqClient = await startRMQClient()
const simController = new SimNosController(
rmqClient
const nosHttpClient = new NosHttpClient(
NOS_BASE_URL
)
rmqClient.consume("sim.nos", simController.recibeMsg)
const simUsecases = new SimNosUsecases(
nosHttpClient
)
const simController = new SimNosController(
simUsecases
)
const simRouter = new SimNosRouter(
simController,
rmqClient
)
rmqClient.consume(RMQ_QUEUE, simRouter.route)
}
startWorker()

View File

@@ -0,0 +1,29 @@
import axios, { AxiosInstance } from "axios";
import { env } from "#config/env/env.js"
export class NosHttpClient {
public client: AxiosInstance;
constructor(
private baseURL: string,
//private jwtManager: JWTProvider<any>
) {
this.client = axios.create({
baseURL: baseURL
})
// Interceptor para los headers fijos
this.client.interceptors.request.use(
async (config) => {
// Configuracion especifica de NOS (El token simepre es el mismo?)
const token = env.NOS_ACCESS_TOKEN;
config.headers.Authorization = `Bearer ${token}`
config.headers.set("content-type", "application/json")
return config
},
(error) => Promise.reject(error)
)
}
}