Inicio port NOS
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
NOS_BASE_URL=localhost
|
NOS_BASE_URL=localhost
|
||||||
|
|
||||||
ENVIORMENT=development
|
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 { ConsumeMessage } from "amqplib";
|
||||||
|
import { SimNosUsecases } from "./SimNOS.usecases";
|
||||||
|
|
||||||
export class SimNosController {
|
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(
|
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) {
|
public activate() {
|
||||||
if (!this.validateActivationMsg(msg)) {
|
return async (msg: ConsumeMessage) => {
|
||||||
throw new Error("Error consumiendo el mensaje no es valido")
|
console.log("Evento activate ", msg)
|
||||||
}
|
|
||||||
|
|
||||||
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 suspend() {
|
||||||
* TODO:
|
return async (msg: ConsumeMessage) => {
|
||||||
* - Loguear motivos de la no validacion
|
console.log("Evento suspend ", msg)
|
||||||
*/
|
}
|
||||||
private validateActivationMsg(msg: ConsumeMessage | null) {
|
}
|
||||||
if (msg == undefined) return false;
|
|
||||||
return true;
|
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),
|
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
||||||
|
|
||||||
// ESPECIFICO NOS
|
// 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 { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js"
|
||||||
import { Channel } from "amqp-connection-manager"
|
import { Channel } from "amqp-connection-manager"
|
||||||
import { env } from "./env/index.js"
|
import { env } from "./env/env.js"
|
||||||
|
|
||||||
const rmqUser = env.RABBITMQ_USER
|
const rmqUser = env.RABBITMQ_USER
|
||||||
const rmqPass = env.RABBITMQ_PASSWORD
|
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 { startRMQClient } from "#config/eventBus.config.js"
|
||||||
|
import { SimNosRouter } from "aplication/SimNOS.router.js"
|
||||||
import { SimNosController } from "./aplication/SimNOS.controller.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() {
|
async function startWorker() {
|
||||||
|
// Instancia de dependencias
|
||||||
|
|
||||||
const rmqClient = await startRMQClient()
|
const rmqClient = await startRMQClient()
|
||||||
const simController = new SimNosController(
|
const nosHttpClient = new NosHttpClient(
|
||||||
rmqClient
|
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()
|
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