Inicio port NOS
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
NOS_BASE_URL=localhost
|
||||
|
||||
ENVIORMENT=development
|
||||
|
||||
NOS_ACCESS_TOKEN=2YGhecTr4+uKbVKxaqBlk2edsrHA2OQY
|
||||
NOS_BASE_URL=https://nosconnectcenter-api.iot-x.com
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
76
packages/sim-consumidor-nos/aplication/SimNOS.router.ts
Normal file
76
packages/sim-consumidor-nos/aplication/SimNOS.router.ts
Normal 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];
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
48
packages/sim-consumidor-nos/domain/NosAPI.ts
Normal file
48
packages/sim-consumidor-nos/domain/NosAPI.ts
Normal 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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
29
packages/sim-consumidor-nos/infrastructure/NosHttpClient.ts
Normal file
29
packages/sim-consumidor-nos/infrastructure/NosHttpClient.ts
Normal 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)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user