diff --git a/docs/sim-nos/Select Page.yml b/docs/sim-nos/Select Page.yml new file mode 100644 index 0000000..4d6611a --- /dev/null +++ b/docs/sim-nos/Select Page.yml @@ -0,0 +1,23 @@ +info: + name: Select Page + type: http + seq: 6 + +http: + method: GET + url: "{{baseurl}}/selectPage" + params: + - name: iccid + value: "8935103196306448300" + type: query + disabled: true + body: + type: json + data: "" + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/docs/sim-nos/Select.yml b/docs/sim-nos/Select.yml new file mode 100644 index 0000000..a701fc8 --- /dev/null +++ b/docs/sim-nos/Select.yml @@ -0,0 +1,25 @@ +info: + name: Select + type: http + seq: 5 + +http: + method: GET + url: "{{baseurl}}/select?iccid=8935103196306448300" + params: + - name: iccid + value: "8935103196306448300" + type: query + body: + type: json + data: |- + { + "iccid": "8933201125068890066" + } + auth: inherit + +settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 diff --git a/docs/sim-nos/environments/local.yml b/docs/sim-nos/environments/local.yml index 7a2fe1b..678d231 100644 --- a/docs/sim-nos/environments/local.yml +++ b/docs/sim-nos/environments/local.yml @@ -1,5 +1,7 @@ name: local color: "#2E8A54" variables: - - name: baseulr - value: http://localhost:3000 + - name: baseurl + value: http://localhost:3001 + - secret: true + name: token diff --git a/packages/sim-consumidor-nos/.env b/packages/sim-consumidor-nos/.env index 3574f36..9864296 100644 --- a/packages/sim-consumidor-nos/.env +++ b/packages/sim-consumidor-nos/.env @@ -1,4 +1,6 @@ NOS_BASE_URL=localhost +APP_PORT=3001 +APP_HOST="0.0.0.0" ENVIORMENT=development diff --git a/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts b/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts index 58866fe..dfcf690 100644 --- a/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts +++ b/packages/sim-consumidor-nos/aplication/SimNOS.controller.ts @@ -1,9 +1,10 @@ import { ConsumeMessage } from "amqplib"; +import { Request, Response } from "express" import { SimNosUsecases } from "./SimNOS.usecases"; import { EventBus } from "sim-shared/domain/EventBus.port.js"; import { Result } from "sim-shared/domain/Result.js"; import { SimEvents } from "sim-shared/domain/SimEvents.js"; -import { error } from "node:console"; +import { iccidValidator } from "./httpValidators"; export class SimNosController { @@ -107,4 +108,73 @@ export class SimNosController { return res; } } + + /** + * Select especificamente por REST para evitar pasar por las colas. + * La respuesta es instantanea no se tiene que registrar como operación. + */ + public selectREST() { + return async (req: Request, res: Response) => { + const { query } = req + const body = { iccid: query.iccid as string } + console.log("Evento select", body) + const validateBody = iccidValidator.validate(body); + + if (validateBody.error != undefined) { + res.status(402).json(validateBody) + return; + } + + const iccid: string | string[] = body.iccid + + console.log("ICCID", iccid) + + if (Array.isArray(iccid)) { + const usecaseRes = this.uscases.selectMany({ iccid }) + } else { + const usecaseRes = await this.uscases.selectOne({ iccid }) + console.log(usecaseRes) + if (usecaseRes.error != undefined) { + res.status(500).json(usecaseRes) + } else { + res.send(usecaseRes.data) + } + return; + } + + res.status(200).json(validateBody) + } + } + + + public selectPageREST() { + return async (req: Request, res: Response) => { + const { query } = req + const body = { + iccid: query.iccid as string + } + console.log("Evento page", body) + /* + const validateBody = iccidValidator.validate(body); + + if (validateBody.error != undefined) { + res.status(402).json(validateBody) + return; + } + */ + const iccid: string | string[] = body.iccid + + console.log("ICCID", iccid) + + const usecaseRes = await this.uscases.selectPage({ iccid }) + + if (usecaseRes.error != undefined) { + res.status(500).json(usecaseRes) + } else { + res.status(200).send(usecaseRes.data) + } + + res.status(200).json({ ok: "true" }) + } + } } diff --git a/packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts b/packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts index bbd2505..aca4794 100644 --- a/packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts +++ b/packages/sim-consumidor-nos/aplication/SimNOS.usecases.ts @@ -56,4 +56,23 @@ export class SimNosUsecases { throw new Error("No hay termination para NOS") } + public async selectOne(args: { + iccid: string + }) { + const res = await this.nosRepository.getLineInfo(args.iccid) + return res + } + + public async selectPage(args: { + + }) { + const res = await this.nosRepository.getLinePage(args) + return res + } + + public selectMany(args: { + iccid: string[] + }) { + return {} + } } diff --git a/packages/sim-consumidor-nos/aplication/httpValidators.ts b/packages/sim-consumidor-nos/aplication/httpValidators.ts new file mode 100644 index 0000000..b452272 --- /dev/null +++ b/packages/sim-consumidor-nos/aplication/httpValidators.ts @@ -0,0 +1,39 @@ +import { BodyValidator, Validator } from "sim-shared/aplication/BodyValidator.js"; + +const iccidNotNull = >{ + field: "iccid", + errorMsg: "El iccid no está definido", + validationFunc: (a: { iccid: unknown }) => { + return (a.iccid != null && a.iccid != undefined) + } +} + +const iccidValueOrArray = >{ + field: "iccid", + errorMsg: "El iccid debe de ser un único valor o una lista", + validationFunc: (a: { iccid: unknown }) => { + return (typeof a.iccid == "string" || Array.isArray(a.iccid)) + } +} + +const iccidLongitudValidator = >{ + field: "iccid", + errorMsg: "La longitud del iccid/s es incorrecta debera ser de 19 caracteres", + validationFunc: (a: { iccid: string | string[] }) => { + if (Array.isArray(a.iccid)) { + const res = (a.iccid as string[]).filter(e => e.length != 19) + if (res.length > 0) return false; + } else { + return (a.iccid as string).length == 19 + } + }, +} + +export const iccidValidator = new BodyValidator<{ iccid: string | string[] }>( + [ + iccidNotNull, + iccidValueOrArray, + iccidLongitudValidator, + ] +) + diff --git a/packages/sim-consumidor-nos/config/env/env.ts b/packages/sim-consumidor-nos/config/env/env.ts index ffbb536..80f0352 100644 --- a/packages/sim-consumidor-nos/config/env/env.ts +++ b/packages/sim-consumidor-nos/config/env/env.ts @@ -30,6 +30,9 @@ export const env = { RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL, RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST), + APP_PORT: Number(process.env.APP_PORT), + APP_HOST: String(process.env.APP_HOST), + // ESPECIFICO NOS NOS_BASE_URL: String(process.env.NOS_BASE_URL), NOS_ACCESS_TOKEN: String(process.env.NOS_ACCESS_TOKEN) diff --git a/packages/sim-consumidor-nos/domain/NosAPI.ts b/packages/sim-consumidor-nos/domain/NosAPI.ts index c093ff5..2bcb728 100644 --- a/packages/sim-consumidor-nos/domain/NosAPI.ts +++ b/packages/sim-consumidor-nos/domain/NosAPI.ts @@ -63,6 +63,10 @@ export namespace NosApi { export type LineDataResponseError = ErrorResponse export type LineDataResponse = LineDataResponseOK | LineDataResponseError + export type PageDataResponseOk = OkResponse + export type PageDataResponseError = OkResponse + export type PageResponse = PageDataResponseOk | PageDataResponseError + export type LineData = { physicalId: string subscriberId: string diff --git a/packages/sim-consumidor-nos/index.ts b/packages/sim-consumidor-nos/index.ts index ac6cfca..31cbcd9 100644 --- a/packages/sim-consumidor-nos/index.ts +++ b/packages/sim-consumidor-nos/index.ts @@ -1,13 +1,17 @@ - +import express from "express" +import cors from 'cors'; 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" +import { NosRepository } from "infrastructure/NosRepository.js" const RMQ_QUEUE = "sim.nos" const NOS_BASE_URL = env.NOS_BASE_URL +const PORT = env.APP_PORT +const HOSTNAME = env.APP_HOST async function startWorker() { // Instancia de dependencias @@ -17,21 +21,47 @@ async function startWorker() { NOS_BASE_URL ) - const simUsecases = new SimNosUsecases( + const nosRepository = new NosRepository( nosHttpClient ) + const simUsecases = new SimNosUsecases( + nosHttpClient, + nosRepository + ) + const simController = new SimNosController( - simUsecases + simUsecases, + rmqClient ) const simRouter = new SimNosRouter( simController, rmqClient - ) + // RMQ rmqClient.consume(RMQ_QUEUE, simRouter.route) + .then(() => console.log("Cliente rmq creado con exito")) + .catch(e => console.error("Error conectando con RABBITMQ", e)) + + // Express + const app = express() + app.use(cors()); + app.use(express.json()); + app.use(express.urlencoded({ extended: true })); + + app.get("/select", simController.selectREST()) + app.get("/selectPage", simController.selectPageREST()) + + app.listen(PORT, HOSTNAME, (e) => { + if (e == undefined) { + console.log("[o] Servidor iniciado en el puerto %d", PORT) + } else { + console.error("Error express ", e) + } + }) + } startWorker() diff --git a/packages/sim-consumidor-nos/infrastructure/NosRepository.ts b/packages/sim-consumidor-nos/infrastructure/NosRepository.ts index 67163a7..b6cc1b4 100644 --- a/packages/sim-consumidor-nos/infrastructure/NosRepository.ts +++ b/packages/sim-consumidor-nos/infrastructure/NosRepository.ts @@ -27,11 +27,11 @@ export class NosRepository { if (axios.isAxiosError(e)) { const error = e as AxiosError return { - error: error.code + " : " + JSON.stringify(error.response) + error: error.code + " : " + String(error.response?.statusText) } } else { return { - error: JSON.stringify(e) + error: String(e) } } } @@ -39,6 +39,59 @@ export class NosRepository { public async getLineInfo(iccid: string): Promise> { const PATH = "/subscribers/" + iccid + console.log("PAth", PATH) + const lineRequest = this.httpClient.get(PATH) + const lineResponse = await this.manageNosRequest(lineRequest) + + if (lineResponse.error != undefined) { + return lineResponse + } else { + return { + data: lineResponse.data.content + } + } + } + + public async getLinePage(args: { + limit?: number, + offset?: number, + filter?: string, + orderBy?: string + }): Promise> { + const PATH = "/subscribers" + + const LIMIT = 100 + const options = { + limit: LIMIT, + offset: 0, + filter: args.filter, + orderBy: args.orderBy + } + + const pageRequest = this.httpClient.get(PATH, { + params: options + }) + + const pageResponse = await this.manageNosRequest(pageRequest) + if (pageResponse.error != undefined) { + return pageResponse + } else { + return { + data: pageResponse.data.content + } + } + } + + public async getLinesInfo(iccid: string[]) /*Promise>*/ { + throw new Error("NOS no permite buscar iccid en bulk, se puede hacer un apaño pero está en proceso") + const PATH = "/subscribers" + const LIMIT = 100 + + const steps = Math.ceil(iccid.length / LIMIT) + const options = { + limit: LIMIT, + offset: 0, + } const req = this.httpClient.post(PATH) const resp = await this.manageNosRequest(req) @@ -47,6 +100,7 @@ export class NosRepository { return resp } else { return { + //@ts-expect-error data: resp.data.content } } diff --git a/packages/sim-consumidor-nos/readme.md b/packages/sim-consumidor-nos/readme.md new file mode 100644 index 0000000..a394778 --- /dev/null +++ b/packages/sim-consumidor-nos/readme.md @@ -0,0 +1,11 @@ +# NOS + +## Particularidades de las operaciones de NOS + +- Documentación de la API: [DOC](https://pelion-help.iot-x.com/nos/en/Content/API/APIReference/API%20Reference.htm?tocpath=_____7) +- No se necesita la pre-activación de las SIM. +- La suspensión y reactivación se llama "bar" y "unbar". +- El token de Authentication dura exactamente 1 año, solo se puede refrescar + desde la web. +- En la documentación la URL de la API es pero la + de producción es . diff --git a/packages/sim-entrada-eventos/index.ts b/packages/sim-entrada-eventos/index.ts index cd3953b..17b2693 100644 --- a/packages/sim-entrada-eventos/index.ts +++ b/packages/sim-entrada-eventos/index.ts @@ -19,7 +19,6 @@ rabbitmqEventBus.connect() console.error("[!] El cliente RMQ no se ha podido iniciar", e) }) - // Middleware app.use(cors()); @@ -39,4 +38,5 @@ app.get("/health", (req, res) => { app.listen(PORT, HOSTNAME, () => { console.log("[o] Servidor iniciado en el puerto %d", PORT) }) + export default {}