Endpoints NOS
This commit is contained in:
23
docs/sim-nos/Select Page.yml
Normal file
23
docs/sim-nos/Select Page.yml
Normal file
@@ -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
|
||||
25
docs/sim-nos/Select.yml
Normal file
25
docs/sim-nos/Select.yml
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
NOS_BASE_URL=localhost
|
||||
APP_PORT=3001
|
||||
APP_HOST="0.0.0.0"
|
||||
|
||||
ENVIORMENT=development
|
||||
|
||||
|
||||
@@ -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" })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
}
|
||||
|
||||
39
packages/sim-consumidor-nos/aplication/httpValidators.ts
Normal file
39
packages/sim-consumidor-nos/aplication/httpValidators.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { BodyValidator, Validator } from "sim-shared/aplication/BodyValidator.js";
|
||||
|
||||
const iccidNotNull = <Validator<{ iccid: unknown }>>{
|
||||
field: "iccid",
|
||||
errorMsg: "El iccid no está definido",
|
||||
validationFunc: (a: { iccid: unknown }) => {
|
||||
return (a.iccid != null && a.iccid != undefined)
|
||||
}
|
||||
}
|
||||
|
||||
const iccidValueOrArray = <Validator<{ iccid: unknown }>>{
|
||||
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 = <Validator<{ iccid: string | string[] }>>{
|
||||
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,
|
||||
]
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -63,6 +63,10 @@ export namespace NosApi {
|
||||
export type LineDataResponseError = ErrorResponse
|
||||
export type LineDataResponse = LineDataResponseOK | LineDataResponseError
|
||||
|
||||
export type PageDataResponseOk = OkResponse<LineData[]>
|
||||
export type PageDataResponseError = OkResponse<LineData[]>
|
||||
export type PageResponse = PageDataResponseOk | PageDataResponseError
|
||||
|
||||
export type LineData = {
|
||||
physicalId: string
|
||||
subscriberId: string
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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<Result<string, NosApi.LineData>> {
|
||||
const PATH = "/subscribers/" + iccid
|
||||
console.log("PAth", PATH)
|
||||
const lineRequest = this.httpClient.get<NosApi.LineDataResponseOK>(PATH)
|
||||
const lineResponse = await this.manageNosRequest<string, NosApi.LineDataResponseOK>(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<Result<string, any>> {
|
||||
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<string, any>(pageRequest)
|
||||
if (pageResponse.error != undefined) {
|
||||
return pageResponse
|
||||
} else {
|
||||
return {
|
||||
data: pageResponse.data.content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async getLinesInfo(iccid: string[]) /*Promise<Result<string, NosApi.LineData>>*/ {
|
||||
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<NosApi.LineDataResponseOK>(PATH)
|
||||
const resp = await this.manageNosRequest<string, NosApi.LineDataResponseOK>(req)
|
||||
@@ -47,6 +100,7 @@ export class NosRepository {
|
||||
return resp
|
||||
} else {
|
||||
return {
|
||||
//@ts-expect-error
|
||||
data: resp.data.content
|
||||
}
|
||||
}
|
||||
|
||||
11
packages/sim-consumidor-nos/readme.md
Normal file
11
packages/sim-consumidor-nos/readme.md
Normal file
@@ -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 <https://nos-api.iot-x.com> pero la
|
||||
de producción es <https://nosconnectcenter-api.iot-x.com>.
|
||||
@@ -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 {}
|
||||
|
||||
Reference in New Issue
Block a user