Compare commits
4 Commits
1.6.0
...
alarmas-ob
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c2e86779a | |||
| 1780cf1743 | |||
| 10104c202f | |||
| ef8222ae30 |
29
.env
Normal file
29
.env
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
PORT=3000
|
||||||
|
API_HOSTNAME=0.0.0.0
|
||||||
|
RABBITMQ_USER=guest
|
||||||
|
RABBITMQ_PASSWORD=guest
|
||||||
|
|
||||||
|
ENVIORMENT=development
|
||||||
|
|
||||||
|
#RABBITMQ_HOST=rabbitmq-sim-broker
|
||||||
|
RABBITMQ_HOST=localhost
|
||||||
|
RABBITMQ_PORT=5672
|
||||||
|
RABBITMQ_USER=guest
|
||||||
|
RABBITMQ_PASSWORD=guest
|
||||||
|
RABBITMQ_SECURE=false
|
||||||
|
RABBITMQ_VHOST=sim-vhost
|
||||||
|
|
||||||
|
# Hay cosas que unificar de varios servicios
|
||||||
|
#POSTGRES_HOST=postgresql-sim
|
||||||
|
POSTGRES_HOST=localhost
|
||||||
|
POSTGRES_DB=postgres
|
||||||
|
POSTGRES_DATABASE=postgres
|
||||||
|
POSTGRES_PORT=5433
|
||||||
|
POSTGRES_USER=postgres
|
||||||
|
POSTGRES_PASSWORD=1234
|
||||||
|
|
||||||
|
# Para el postgres local para generar el script de resultado de migraciones
|
||||||
|
PGHOST=localhost
|
||||||
|
PGUSER=alvar
|
||||||
|
PGPASSWORD=alvar
|
||||||
|
PGPORT=5433
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -16,11 +16,7 @@ node_modules
|
|||||||
#!.yarn/cache
|
#!.yarn/cache
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
# Certificados
|
|
||||||
*.pem
|
*.pem
|
||||||
*.p12
|
|
||||||
*.key
|
|
||||||
|
|
||||||
dist/*
|
dist/*
|
||||||
|
|
||||||
.env
|
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -12,13 +12,13 @@ La compañia a la que pertenece cada peticion y por tanto el servicio que lo va
|
|||||||
|
|
||||||
## Decisiones pendientes
|
## Decisiones pendientes
|
||||||
|
|
||||||
- [x] La capa worker según acción y la de operaciones de proveedores, se podrían unir en una sola con un enrutamiento por acción y compañía, pasando de tener claves `sim.[acción]` a `sim.[compañia].[acción]`. _Se ha aplicado el cambio ahora las routing keys tienen la estructura `sim.[compañia].[acción]`_
|
- [x] La capa worker según acción y la de operaciones de proveedores, se podrían unir en una sola con un enrutamiento por acción y compañía, pasando de tener claves `sim.[acción]` a `sim.[compañia].[acción]`. *Se ha aplicado el cambio ahora las routing keys tienen la estructura `sim.[compañia].[acción]`*
|
||||||
- [x] La estructura de RMQ se genera por medio del JSON, igual habría que definir cada cola en el worker que la consuma para poder añadir workers sin parar el RMQ. _Se ha aplicado el cambio, ahora solo se define en el json el broker principal para garantizar que exita sin servicios consumidores. Sin embargo tal como estan estructurdos los proyectos no es posible reiniciar solo un servicio_
|
- [x] La estructura de RMQ se genera por medio del JSON, igual habría que definir cada cola en el worker que la consuma para poder añadir workers sin parar el RMQ. *Se ha aplicado el cambio, ahora solo se define en el json el broker principal para garantizar que exita sin servicios consumidores. Sin embargo tal como estan estructurdos los proyectos no es posible reiniciar solo un servicio*
|
||||||
- [ ] Versionado de la API.
|
- [ ] Versionado de la API.
|
||||||
- [x] Método para sacar la compañía a partir del iccid, o buscar en la BDD si no es posible. _De momento es un objeto Map en el servicio de gateway_
|
- [x] Método para sacar la compañía a partir del iccid, o buscar en la BDD si no es posible. *De momento es un objeto Map en el servicio de gateway*
|
||||||
- [ ] Cola de mensajes que no se han podido procesar. Distinguir según error de red; se reintenta; o error del propio mensaje; se envía a la cola de errores. v2 Se ha creado una cola de delay pero no se distingue el tipo de error, después de n reintentos el mensaje va a la cola de dead-letter.
|
- [ ] Cola de mensajes que no se han podido procesar. Distinguir según error de red; se reintenta; o error del propio mensaje; se envía a la cola de errores. v2 Se ha creado una cola de delay pero no se distingue el tipo de error, despues de n reintentos el mensaje va a la cola de dead-letter.
|
||||||
- [x] Seguimiento de las peticiones de Objenious, por cada peticion hay qye hacer un seguimiento del request y de los mass action para saber si las activaciones han tenido exito. Habria que crear otra cola para consultar cada x tiempo o mejor un cron?
|
- [ ] Seguimiento de las peticiones de Objenious, por cada peticion hay qye hacer un seguimiento del request y de los mass action para saber si las activaciones han tenido exito. Habria que crear otra cola para consultar cada x tiempo o mejor un cron?
|
||||||
- [x] Actualizar en la base de datos el estado de las peticiones de las sim y añadir el número de telefono cuando se activen o cuando se cumpla una accion.
|
- [ ] Actualizar en la base de datos el estado de las peticiones de las sim y añadir el número de telefono cuando se activen o cuando se cumpla una accion.
|
||||||
|
|
||||||
## Versión con consumidores basados en la compañia
|
## Versión con consumidores basados en la compañia
|
||||||
|
|
||||||
@@ -32,15 +32,8 @@ OBJENIOUS (33)2011a
|
|||||||
|
|
||||||
## Diagrama de las colas de Rabbitmq
|
## Diagrama de las colas de Rabbitmq
|
||||||
|
|
||||||
Actualmente la topología de las colas consiste en un exchage principal que recibe todos los mensajes y los redistribuye en las colas de cada empresa y a la de logs. Para evitar reintentos de mensajes instantáneos, que podrían ser inútiles si algún servicio se ha caído, se ha añadido una cola de delay que almacena los mensajes fallidos durante n segundos antes de ser reenviados al exchange principal. Si después de n reintentos el mensaje sigue fallando se envía a la cola de dead-letter para ser procesado manualmente.
|
Actualmente la topologia de las colas consiste en un exchage principal que recibe todos los mensajes y los redistribuye en las colas de cada empresa y a la de logs. Para evitar reintentos de mensajes instantaneos, que podrian ser inutiles si algún servicio se ha caido, se ha añadido una cola de delay que alamcena los mesajes fallidos durante n segundos antes de ser reenviados al exchange principal. Si despues de n reintentos el mensaje sigue fallando se envia a la cola de dead-letter para ser procesado manualmente.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
La decisión del numero de reintentos y la cola de dlx se hace en los servicios, con una configuración global en shared.
|
La decisión del numero de reintentos y la cola de dlx se hace en los servicios, con una configuracion global en shared.
|
||||||
|
|
||||||
## Puertos internos para comunicaciones entre sub-servicios
|
|
||||||
|
|
||||||
- **3000**: Gateway (sim-entrada-eventos)
|
|
||||||
- **3001**: Consumidor NOS (sim-consumidor-nos)
|
|
||||||
- **3002**: Consumidor Objenious (sim-consumidor-objenious)
|
|
||||||
- **3003**: Consumidor Alai (sim-consumidor-alai)
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ CREATE TABLE IF NOT EXISTS order_tracking (
|
|||||||
payload JSONB, -- Duda si es optimo guardar la copia, es útil en caso de fallo
|
payload JSONB, -- Duda si es optimo guardar la copia, es útil en caso de fallo
|
||||||
|
|
||||||
-- Campos de reintentos?
|
-- Campos de reintentos?
|
||||||
|
|
||||||
status order_status NOT NULL DEFAULT 'pending',
|
status order_status NOT NULL DEFAULT 'pending',
|
||||||
retry_count INT DEFAULT 0,
|
retry_count INT DEFAULT 0,
|
||||||
error_message TEXT, -- Razón del fallo
|
error_message TEXT, -- Razón del fallo
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
/**
|
|
||||||
* Para la tarea WEBINT-328-Pausas-cacelaciones.
|
|
||||||
* Almacena las pausas/cancelaciones que no se han podido hacer porque la linea esta en
|
|
||||||
* "Test"
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
DO $$ BEGIN
|
|
||||||
CREATE TYPE SUSPENDTERMINATE AS ENUM ('suspend','terminate');
|
|
||||||
EXCEPTION
|
|
||||||
WHEN duplicate_object THEN null;
|
|
||||||
END $$;
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS pause_cancel_tasks (
|
|
||||||
id BIGINT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
|
|
||||||
iccid TEXT NOT NULL,
|
|
||||||
operation_type SUSPENDTERMINATE,
|
|
||||||
last_checked TIMESTAMPTZ, -- Última vez que se ha comprobado que no esté en test
|
|
||||||
activation_date TIMESTAMPTZ, -- Fecha de activacion para comprobar si ha pasdo un mes
|
|
||||||
next_check TIMESTAMPTZ, -- Si se ha comprobado se asignará la siguiente fecha de revision
|
|
||||||
|
|
||||||
completed_date TIMESTAMPTZ, -- Cuando se ha completado, para bien o mal.
|
|
||||||
error TEXT,
|
|
||||||
action_data JSONB -- datos de la operacion original.
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Indice de las tareas que no han terminado
|
|
||||||
CREATE INDEX idx_pause_cancel_tasks_pending
|
|
||||||
ON pause_cancel_tasks (next_check)
|
|
||||||
WHERE completed_date IS NULL;
|
|
||||||
|
|
||||||
|
|
||||||
@@ -8,8 +8,6 @@ RUN corepack enable
|
|||||||
COPY ./dist/packages ./packages
|
COPY ./dist/packages ./packages
|
||||||
COPY ./.yarnrc.yml ./
|
COPY ./.yarnrc.yml ./
|
||||||
COPY ./docs ./docs
|
COPY ./docs ./docs
|
||||||
# Para las migraciones
|
|
||||||
COPY ./deployment ./deployment
|
|
||||||
|
|
||||||
COPY ./package.json ./
|
COPY ./package.json ./
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
internal:
|
internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
volumes:
|
|
||||||
rabbitmq_data:
|
|
||||||
|
|
||||||
services:
|
services:
|
||||||
rabbitmq-sim-broker:
|
rabbitmq-sim-broker:
|
||||||
@@ -30,7 +28,6 @@ services:
|
|||||||
entrypoint: ["bash", "/usr/local/bin/docker-entrypoint-wrapper.sh"]
|
entrypoint: ["bash", "/usr/local/bin/docker-entrypoint-wrapper.sh"]
|
||||||
command: ["rabbitmq-server"]
|
command: ["rabbitmq-server"]
|
||||||
volumes:
|
volumes:
|
||||||
- rabbitmq_data:/var/lib/rabbitmq
|
|
||||||
- ./rabbit/docker-entrypoint-wrapper.sh:/usr/local/bin/docker-entrypoint-wrapper.sh:ro
|
- ./rabbit/docker-entrypoint-wrapper.sh:/usr/local/bin/docker-entrypoint-wrapper.sh:ro
|
||||||
- ./rabbitmq_plugins/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro
|
- ./rabbitmq_plugins/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro
|
||||||
- ./rabbit/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
|
- ./rabbit/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
|
||||||
@@ -75,10 +72,7 @@ services:
|
|||||||
- ${PORT}
|
- ${PORT}
|
||||||
volumes:
|
volumes:
|
||||||
- ./.env:/home/node/app/.env:ro
|
- ./.env:/home/node/app/.env:ro
|
||||||
- ./sim-consumidor-nos.env:/home/node/app/packages/sim-consumidor-nos/.env:ro
|
|
||||||
- ./sim-consumidor-alai.env:/home/node/app/packages/sim-consumidor-alai/.env:ro
|
|
||||||
- ./sim-consumidor-objenious.env:/home/node/app/packages/sim-consumidor-objenious/.env:ro
|
- ./sim-consumidor-objenious.env:/home/node/app/packages/sim-consumidor-objenious/.env:ro
|
||||||
- ./wsaccess_alaisecure_com_cert_client_new.p12:/home/node/app/packages/sim-consumidor-alai/certificates/wsaccess_alaisecure_com_cert_client_new.p12:ro
|
|
||||||
- ./sim-objenious-cron.env:/home/node/app/packages/sim-objenious-cron/.env:ro
|
- ./sim-objenious-cron.env:/home/node/app/packages/sim-objenious-cron/.env:ro
|
||||||
- ./obj.pem:/home/node/app/packages/sim-consumidor-objenious/obj.pem:ro
|
- ./obj.pem:/home/node/app/packages/sim-consumidor-objenious/obj.pem:ro
|
||||||
- ./obj.pem:/home/node/app/packages/sim-objenious-cron/obj.pem:ro
|
- ./obj.pem:/home/node/app/packages/sim-objenious-cron/obj.pem:ro
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
cd /home
|
cd /home
|
||||||
|
|
||||||
cd /home/node/app
|
cd /home/node/app && yarn start
|
||||||
yarn migrate
|
|
||||||
yarn start
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pipeline {
|
|||||||
stage("🧱 Building") {
|
stage("🧱 Building") {
|
||||||
steps {
|
steps {
|
||||||
sh 'rm -rf dist/'
|
sh 'rm -rf dist/'
|
||||||
sh 'yarn run build:prod'
|
sh 'yarn run build'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage("🏗 Deploying") {
|
stage("🏗 Deploying") {
|
||||||
@@ -38,40 +38,14 @@ pipeline {
|
|||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
execCommand: "mkdir -p $APP_REMOTE_PATH"
|
execCommand: "mkdir -p $APP_REMOTE_PATH"
|
||||||
),
|
),
|
||||||
sshTransfer(
|
|
||||||
cleanRemote: false,
|
|
||||||
execCommand: "rm -rf $APP_REMOTE_PATH/dist"
|
|
||||||
),
|
|
||||||
sshTransfer(
|
|
||||||
cleanRemote: false,
|
|
||||||
execCommand: "ls -la $BASE_REMOTE_PATH/vault/savefamily/sf-sims/"
|
|
||||||
),
|
|
||||||
sshTransfer(
|
|
||||||
cleanRemote: false,
|
|
||||||
remoteDirectory: "$APP_REMOTE_PATH",
|
|
||||||
sourceFiles: "dist/**/*",
|
|
||||||
excludes: "dist/**/node_modules/**"
|
|
||||||
),
|
|
||||||
sshTransfer(
|
sshTransfer(
|
||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/.env $APP_REMOTE_PATH/.env"
|
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/.env $APP_REMOTE_PATH/.env"
|
||||||
),
|
),
|
||||||
sshTransfer(
|
|
||||||
cleanRemote: false,
|
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/wsaccess_alaisecure_com_cert_client_new.p12 $APP_REMOTE_PATH/wsaccess_alaisecure_com_cert_client_new.p12"
|
|
||||||
),
|
|
||||||
sshTransfer(
|
sshTransfer(
|
||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/sim-consumidor-objenious.env $APP_REMOTE_PATH/sim-consumidor-objenious.env"
|
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/sim-consumidor-objenious.env $APP_REMOTE_PATH/sim-consumidor-objenious.env"
|
||||||
),
|
),
|
||||||
sshTransfer(
|
|
||||||
cleanRemote: false,
|
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/sim-consumidor-nos.env $APP_REMOTE_PATH/sim-consumidor-nos.env"
|
|
||||||
),
|
|
||||||
sshTransfer(
|
|
||||||
cleanRemote: false,
|
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/sim-consumidor-alai.env $APP_REMOTE_PATH/sim-consumidor-alai.env"
|
|
||||||
),
|
|
||||||
sshTransfer(
|
sshTransfer(
|
||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/sim-objenious-cron.env $APP_REMOTE_PATH/sim-objenious-cron.env"
|
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/sim-objenious-cron.env $APP_REMOTE_PATH/sim-objenious-cron.env"
|
||||||
@@ -80,6 +54,12 @@ pipeline {
|
|||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/obj.pem $APP_REMOTE_PATH/obj.pem"
|
execCommand: "ln -sf $BASE_REMOTE_PATH/vault/savefamily/sf-sims/obj.pem $APP_REMOTE_PATH/obj.pem"
|
||||||
),
|
),
|
||||||
|
sshTransfer(
|
||||||
|
cleanRemote: false,
|
||||||
|
remoteDirectory: "$APP_REMOTE_PATH",
|
||||||
|
sourceFiles: "dist/**/*",
|
||||||
|
excludes: "dist/**/node_modules/**"
|
||||||
|
),
|
||||||
sshTransfer(
|
sshTransfer(
|
||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
remoteDirectory: "$APP_REMOTE_PATH",
|
remoteDirectory: "$APP_REMOTE_PATH",
|
||||||
@@ -89,6 +69,7 @@ pipeline {
|
|||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
remoteDirectory: "$APP_REMOTE_PATH",
|
remoteDirectory: "$APP_REMOTE_PATH",
|
||||||
sourceFiles: "deployment/database/**/*",
|
sourceFiles: "deployment/database/**/*",
|
||||||
|
removePrefix: "deployment",
|
||||||
),
|
),
|
||||||
sshTransfer(
|
sshTransfer(
|
||||||
cleanRemote: false,
|
cleanRemote: false,
|
||||||
|
|||||||
@@ -1,92 +1,90 @@
|
|||||||
{
|
{
|
||||||
"rabbit_version": "4.2.2",
|
"rabbit_version": "4.2.2",
|
||||||
"rabbitmq_version": "4.2.2",
|
"rabbitmq_version": "4.2.2",
|
||||||
"product_name": "RabbitMQ",
|
"product_name": "RabbitMQ",
|
||||||
"product_version": "4.2.2",
|
"product_version": "4.2.2",
|
||||||
"users": [
|
"users": [
|
||||||
{
|
{
|
||||||
"name": "RABBITMQ_USER_PLACEHOLDER",
|
"name": "RABBITMQ_USER_PLACEHOLDER",
|
||||||
"password": "RABBITMQ_PASSWORD_PLACEHOLDER",
|
"password": "RABBITMQ_PASSWORD_PLACEHOLDER",
|
||||||
"tags": [
|
"tags": ["administrator"]
|
||||||
"administrator"
|
}
|
||||||
]
|
],
|
||||||
}
|
"vhosts": [
|
||||||
],
|
{
|
||||||
"vhosts": [
|
"name": "sim-vhost"
|
||||||
{
|
}
|
||||||
"name": "sim-vhost"
|
],
|
||||||
}
|
"permissions": [
|
||||||
],
|
{
|
||||||
"permissions": [
|
"user": "RABBITMQ_USER_PLACEHOLDER",
|
||||||
{
|
"vhost": "sim-vhost",
|
||||||
"user": "RABBITMQ_USER_PLACEHOLDER",
|
"configure": ".*",
|
||||||
"vhost": "sim-vhost",
|
"write": ".*",
|
||||||
"configure": ".*",
|
"read": ".*"
|
||||||
"write": ".*",
|
}
|
||||||
"read": ".*"
|
],
|
||||||
}
|
"topic_permissions": [],
|
||||||
],
|
"parameters": [],
|
||||||
"topic_permissions": [],
|
"global_parameters": [
|
||||||
"parameters": [],
|
{
|
||||||
"global_parameters": [
|
"name": "cluster_name",
|
||||||
{
|
"value": "rabbit@a8d5c6e08439"
|
||||||
"name": "cluster_name",
|
},
|
||||||
"value": "rabbit@a8d5c6e08439"
|
{
|
||||||
},
|
"name": "internal_cluster_id",
|
||||||
{
|
"value": "rabbitmq-cluster-id-gXeBLbsUC2W2tU0Bx_QY_w"
|
||||||
"name": "internal_cluster_id",
|
}
|
||||||
"value": "rabbitmq-cluster-id-gXeBLbsUC2W2tU0Bx_QY_w"
|
],
|
||||||
}
|
"policies": [
|
||||||
],
|
{
|
||||||
"policies": [
|
"vhost": "sim-vhost",
|
||||||
{
|
"name": "pol.sim.dlx",
|
||||||
"vhost": "sim-vhost",
|
"pattern": "sim.*",
|
||||||
"name": "pol.sim.dlx",
|
"apply-to": "queues",
|
||||||
"pattern": "sim.*",
|
"definition": {
|
||||||
"apply-to": "queues",
|
"dead-letter-exchange": "sim.dlx"
|
||||||
"definition": {
|
},
|
||||||
"dead-letter-exchange": "sim.dlx"
|
"priority": 7
|
||||||
},
|
}
|
||||||
"priority": 7
|
],
|
||||||
}
|
"exchanges": [
|
||||||
],
|
{
|
||||||
"exchanges": [
|
"name": "sim.exchange",
|
||||||
{
|
"vhost": "sim-vhost",
|
||||||
"name": "sim.exchange",
|
"type": "topic",
|
||||||
"vhost": "sim-vhost",
|
"durable": true,
|
||||||
"type": "topic",
|
"auto_delete": false,
|
||||||
"durable": true,
|
"internal": false,
|
||||||
"auto_delete": false,
|
"argurments": {}
|
||||||
"internal": false,
|
},
|
||||||
"argurments": {}
|
{
|
||||||
},
|
"name": "sim.dlx",
|
||||||
{
|
"vhost": "sim-vhost",
|
||||||
"name": "sim.dlx",
|
"type": "topic",
|
||||||
"vhost": "sim-vhost",
|
"durable": true,
|
||||||
"type": "topic",
|
"auto_delete": false,
|
||||||
"durable": true,
|
"internal": false,
|
||||||
"auto_delete": false,
|
"argurments": {}
|
||||||
"internal": false,
|
}
|
||||||
"argurments": {}
|
],
|
||||||
}
|
"queues": [
|
||||||
],
|
{
|
||||||
"queues": [
|
"name": "sim.logs",
|
||||||
{
|
"vhost": "sim-vhost",
|
||||||
"name": "sim.logs",
|
"durable": true,
|
||||||
"vhost": "sim-vhost",
|
"auto_delete": false,
|
||||||
"durable": true,
|
"arguments": {}
|
||||||
"auto_delete": false,
|
}
|
||||||
"arguments": {}
|
],
|
||||||
}
|
"bindings": [
|
||||||
],
|
{
|
||||||
"bindings": [
|
"source": "sim.exchange",
|
||||||
{
|
"vhost": "sim-vhost",
|
||||||
"source": "sim.exchange",
|
"destination": "sim.logs",
|
||||||
"vhost": "sim-vhost",
|
"destination_type": "queue",
|
||||||
"destination": "sim.logs",
|
"routing_key": "sim.#",
|
||||||
"destination_type": "queue",
|
"arguments": {}
|
||||||
"routing_key": "sim.#",
|
}
|
||||||
"arguments": {}
|
]
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,6 @@ networks:
|
|||||||
services:
|
services:
|
||||||
rabbitmq-sim-broker:
|
rabbitmq-sim-broker:
|
||||||
container_name: rabbitmq-sim-broker
|
container_name: rabbitmq-sim-broker
|
||||||
hostname: rabbitmq-sim
|
|
||||||
image: "rabbitmq:4.2.2-management"
|
image: "rabbitmq:4.2.2-management"
|
||||||
ports:
|
ports:
|
||||||
- "5672:5672"
|
- "5672:5672"
|
||||||
@@ -24,7 +23,6 @@ services:
|
|||||||
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
|
RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
|
||||||
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
|
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
|
||||||
volumes:
|
volumes:
|
||||||
- ./rabbitmq-data/:/var/lib/rabbitmq/
|
|
||||||
- ./rabbitmq_plugins/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro
|
- ./rabbitmq_plugins/enabled_plugins:/etc/rabbitmq/enabled_plugins:ro
|
||||||
- ./deployment/local/rabbit/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
|
- ./deployment/local/rabbit/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf:ro
|
||||||
- ./deployment/local/rabbit/definitions.json:/etc/rabbitmq/definitions.json:ro
|
- ./deployment/local/rabbit/definitions.json:/etc/rabbitmq/definitions.json:ro
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
info:
|
|
||||||
name: Change External ID
|
|
||||||
type: http
|
|
||||||
seq: 7
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/v1/subscription/{{subscription}}?action=MODIFY"
|
|
||||||
params:
|
|
||||||
- name: action
|
|
||||||
value: MODIFY
|
|
||||||
type: query
|
|
||||||
body:
|
|
||||||
type: json
|
|
||||||
data: |-
|
|
||||||
{
|
|
||||||
"externalID":""
|
|
||||||
}
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{alai_token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: subscription
|
|
||||||
value: asdasdasd
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: IMEI of subscription
|
|
||||||
type: http
|
|
||||||
seq: 5
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/v1/subscription/{{subscription}}/imei"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{alai_token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: subscription
|
|
||||||
value: SID1848557_TS1766417781101_0
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
info:
|
|
||||||
name: Login
|
|
||||||
type: http
|
|
||||||
seq: 2
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: POST
|
|
||||||
url: "{{baseurl}}/v1/auth/login"
|
|
||||||
body:
|
|
||||||
type: json
|
|
||||||
data: |-
|
|
||||||
{
|
|
||||||
"username": "{{username}}",
|
|
||||||
"password": "{{password}}",
|
|
||||||
"brandID": "{{brandId}}"
|
|
||||||
}
|
|
||||||
auth: inherit
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
scripts:
|
|
||||||
- type: after-response
|
|
||||||
code: |-
|
|
||||||
const data = res.getBody();
|
|
||||||
|
|
||||||
if (data.status != 200) {
|
|
||||||
console.error("Error de login: ", data)
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data && data.accessToken) {
|
|
||||||
|
|
||||||
bru.setEnvVar("alai_token", data.accessToken);
|
|
||||||
|
|
||||||
if (data.tokenType) {
|
|
||||||
bru.setEnvVar("alai_token_type", data.tokenType);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("Token guardado correctamente");
|
|
||||||
} else {
|
|
||||||
console.error("No se pudo encontrar el accessToken en la respuesta");
|
|
||||||
}
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
|
|
||||||
docs: |-
|
|
||||||
Necesita un certificado p12 (PFX) y la contraseña asociada para efectuar la operacion.
|
|
||||||
Collection Settings => ClientCertificates => Add Certificate
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
info:
|
|
||||||
name: New Order
|
|
||||||
type: http
|
|
||||||
seq: 3
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: POST
|
|
||||||
url: "{{baseurl}}/v1/order"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: SIM
|
|
||||||
type: http
|
|
||||||
seq: 4
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/v1/sim/{{iccid}}"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{alai_token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: iccid
|
|
||||||
value: "8934909001500561503"
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: Subscription
|
|
||||||
type: http
|
|
||||||
seq: 4
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/v1/subscription/{{subscription}}"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{alai_token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: subscription
|
|
||||||
value: SID1776275_TS1759238704226_0
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
Binary file not shown.
@@ -1,7 +0,0 @@
|
|||||||
name: local
|
|
||||||
color: "#2E8A54"
|
|
||||||
variables:
|
|
||||||
- name: baseurl
|
|
||||||
value: http://localhost:3002
|
|
||||||
- secret: true
|
|
||||||
name: token
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
name: prod
|
|
||||||
color: "#CE4F3B"
|
|
||||||
variables:
|
|
||||||
- name: baseurl
|
|
||||||
value: https://wsaccess.alaisecure.com/bssrest
|
|
||||||
- name: username
|
|
||||||
value: palomaibanez
|
|
||||||
- name: password
|
|
||||||
value: palomaibanez123
|
|
||||||
- secret: true
|
|
||||||
name: certPasswd
|
|
||||||
- name: brandId
|
|
||||||
value: savefamily
|
|
||||||
- name: alai_token
|
|
||||||
value: eyJhbGciOiJIUzM4NCJ9.eyJiciI6InNhdmVmYW1pbHkiLCJpcCI6Ijg4LjE1LjE1Ny4xNjciLCJzdWIiOiJwYWxvbWFpYmFuZXoiLCJzIjoiRVdTMTY0YWJhYWRlNjA3ZDAyIiwicG9zIjoic2F2ZWZhbWlseUNhYyIsImlkV3NVc2VyIjoiODYiLCJpc012bmEiOmZhbHNlLCJkb21haW4iOiJBbGFpfHNhdmVmYW1pbHkiLCJpYXQiOjE3NzgxNTEzMzYsImV4cCI6MTc3ODE2MjEzNn0.zCFBJJsa0Krc7n5vUFF00z9Tq7m0dRlCGzs2Od67jaLCCn-mnIyyU424PkazacRW
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
opencollection: 1.0.0
|
|
||||||
|
|
||||||
info:
|
|
||||||
name: sim-alai
|
|
||||||
config:
|
|
||||||
proxy:
|
|
||||||
inherit: true
|
|
||||||
config:
|
|
||||||
protocol: http
|
|
||||||
hostname: ""
|
|
||||||
port: ""
|
|
||||||
auth:
|
|
||||||
username: ""
|
|
||||||
password: ""
|
|
||||||
bypassProxy: ""
|
|
||||||
clientCertificates:
|
|
||||||
- domain: wsaccess.alaisecure.com
|
|
||||||
type: pkcs12
|
|
||||||
pkcs12FilePath: certificates\alai_cert.p12
|
|
||||||
passphrase: iHaaek+zyzWz6cH6rg==
|
|
||||||
bundled: false
|
|
||||||
extensions:
|
|
||||||
bruno:
|
|
||||||
ignore:
|
|
||||||
- node_modules
|
|
||||||
- .git
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -6,80 +6,16 @@ meta {
|
|||||||
|
|
||||||
post {
|
post {
|
||||||
url: {{baseurl}}/sim/activate
|
url: {{baseurl}}/sim/activate
|
||||||
body: json
|
body: formUrlEncoded
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
body:json {
|
|
||||||
{
|
|
||||||
"iccid": "1234"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 123
|
iccid: 8933201125068886692
|
||||||
offer: mensual
|
offer: SAVEFAMILY1
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
timeout: 0
|
timeout: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
docs {
|
|
||||||
Campos de entrada:
|
|
||||||
```ts
|
|
||||||
// Header requerido
|
|
||||||
// > content-type:application/x-www-form-urlencoded
|
|
||||||
// > content-type:application/json
|
|
||||||
// Cualquiera de los 2 es valido
|
|
||||||
|
|
||||||
// Esquema body
|
|
||||||
{
|
|
||||||
iccid: string,
|
|
||||||
offer: "mensual" | "anual" | "SAVEFAMILY1" | "SAVEFAMILY2"
|
|
||||||
webhook?: string,
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
En el campo `offer` "mensual" equivale a "SAVEFAMILY2" y "anual" a "SAVEFAMILY1" porque se mantien los códigos de Oferta de Objenious por compatibilidad pero se espera usar "mensual" y "anual" y hacer la conversión en el servicio de cada proveedor.
|
|
||||||
|
|
||||||
Para las llamadas al webhook se va a usar siempre el metodo `POST`, ahora mismo no se firman los mensajes. Se introduce la URL completa tal que `https://dominion.com/v1/endpoint`.
|
|
||||||
|
|
||||||
Respuestas:
|
|
||||||
- **200**: OK
|
|
||||||
``` ts
|
|
||||||
// Esquema
|
|
||||||
{
|
|
||||||
iccid: string,
|
|
||||||
operation: string,
|
|
||||||
message_id: string, //uuidv7
|
|
||||||
}
|
|
||||||
```
|
|
||||||
``` json
|
|
||||||
// Ejemplo
|
|
||||||
{
|
|
||||||
"iccid": "89332011250651xxxxx",
|
|
||||||
"operation": "activation",
|
|
||||||
"message_id": "019dbeaf-8abb-7783-8b51-94fbd9f0b0df"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
*iccid*: Confirmación del iccid enviado.
|
|
||||||
*operation*: Confirmación de la operacion que se ha aplicado.
|
|
||||||
*message_id*: Id de la operación, para consultar en orders.
|
|
||||||
|
|
||||||
> A futuro se va a incluir un campo `"ref":[]` para añadir los enlaces a las consultas de la operación. El body va a permitir tambien json.
|
|
||||||
|
|
||||||
- **402**: Algún campo es incorrecto
|
|
||||||
Se indica que campo es incorrecto, si hubiese mas de uno solo aparecería el primero en comprobarse.
|
|
||||||
```json
|
|
||||||
"errors": {
|
|
||||||
"msg": "La longitud del iccid es incorrecta debera ser de 19 caracteres",
|
|
||||||
"field": "iccid"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
- **500**: Error general
|
|
||||||
Ha ocurrido un error imprevisto durante la
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Activation Email Health
|
name: Activation Email Health
|
||||||
type: http
|
type: http
|
||||||
seq: 9
|
seq: 8
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Activation Email
|
name: Activation Email
|
||||||
type: http
|
type: http
|
||||||
seq: 8
|
seq: 6
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Preactivate
|
|
||||||
type: http
|
|
||||||
seq: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseAlai}}/preactivate?iccid=8934909001400027654
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
iccid: 8934909001400027654
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Select SIM
|
|
||||||
type: http
|
|
||||||
seq: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseAlai}}/select/?iccid=8934909001500561503
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
iccid: 8934909001500561503
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Alai
|
|
||||||
seq: 14
|
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
mode: inherit
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Cancel
|
name: Cancel
|
||||||
type: http
|
type: http
|
||||||
seq: 4
|
seq: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
@@ -11,7 +11,7 @@ post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125068889894
|
iccid: 8933201125068886692
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Docs
|
name: Docs
|
||||||
type: http
|
type: http
|
||||||
seq: 11
|
seq: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Get pending orders
|
name: Get pending orders
|
||||||
type: http
|
type: http
|
||||||
seq: 10
|
seq: 11
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Health
|
name: Health
|
||||||
type: http
|
type: http
|
||||||
seq: 7
|
seq: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Select
|
|
||||||
type: http
|
|
||||||
seq: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseurl}}/portugal/select
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Nos
|
|
||||||
seq: 15
|
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
mode: inherit
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: France Suspended Lines
|
|
||||||
type: http
|
|
||||||
seq: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseurl}}/france/lines?status=SUSPENDED&limit=100
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
status: SUSPENDED
|
|
||||||
limit: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
vars:pre-request {
|
|
||||||
iccid: 8933201125065160331
|
|
||||||
~baseurl: http://localhost:3002
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: France Suspended Time
|
|
||||||
type: http
|
|
||||||
seq: 15
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseurl}}/france/lines/{{iccid}}/suspended-time
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
vars:pre-request {
|
|
||||||
iccid: 8933201125065160331
|
|
||||||
~baseurl: http://localhost:3002
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Objenious
|
|
||||||
seq: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
mode: inherit
|
|
||||||
}
|
|
||||||
@@ -5,7 +5,7 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{baseurl}}/orders/019dbeaf-8abb-7783-8b51-94fbd9f0b0df
|
url: {{baseurl}}/orders/
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,15 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{baseurl}}/orders/message_id/019dbeaf-8abb-7783-8b51-94fbd9f0b0df
|
url: {{baseurl}}/orders/message_id/019c93d3-014a-711d-b958-03dd629be78d
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
params:query {
|
||||||
|
~message_id: 019c93d3-014a-711d-b958-03dd629be78d
|
||||||
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
encodeUrl: true
|
encodeUrl: true
|
||||||
timeout: 0
|
timeout: 0
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Orders
|
|
||||||
seq: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
mode: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
docs {
|
|
||||||
# Orders
|
|
||||||
|
|
||||||
Los *order* representan ordenes que se hacen al servidor y representan en que estado se encuentran las peticiones. Los *order* se generan cuando se solicita una operacion y devuelven su identificador en el campo `message_id` de todas las respuestas a peticiones que requieran cambios. Los identificadores de `order` son UUIDv7, aunque tambien tienen asociado un id tradicional BIGINT en la BDD.
|
|
||||||
|
|
||||||
## Ciclo de vida
|
|
||||||
|
|
||||||
Cuando se crea un *order* comienza en estado `pending`, inicando que ha entrado en la cola y está pendiente de iniciarse; una vez se ha consumido por un servicio pasa a estado `running` indicando que la operacion asociada al *order* ha comenzado, el order continuara en este estado durante un tiempo indefinido (pueden pasar semanas para algunos casos), hasta que la tara finalize correctamente o con errores. En el caso que la tarea finalize con éxito el *order* pasará a estado `finished`, en caso de que haya habido un error el estado será `failed` y se almacenará el error en los campos `error_message` y opcionalemente en `error_stacktrace` según gravedad del error.
|
|
||||||
|
|
||||||
- Caso normal
|
|
||||||
`pending` -> `running` -> `finished`
|
|
||||||
|
|
||||||
- Error durante el consumo
|
|
||||||
`pending` -> `failed`
|
|
||||||
|
|
||||||
- Error durante la operacion
|
|
||||||
`pending` -> `running` -> `failed`
|
|
||||||
|
|
||||||
## Endpoints
|
|
||||||
Estan sujetos a cambios en cuanto a mostrar información
|
|
||||||
|
|
||||||
- [WIP]**GET** /orders?{query}
|
|
||||||
Devuelve todos los orders con un campo que tenga el valor especificado en la query
|
|
||||||
|
|
||||||
- **GET** /orders/{id}
|
|
||||||
Devuelve el order objetivo según su UUID de mensaje (No según el uuid de mensaje)
|
|
||||||
|
|
||||||
- **GET** /orders/base_id/{id}
|
|
||||||
Devuelve el id según su id de la bdd, no es el metodo normal de usar la api
|
|
||||||
|
|
||||||
- **GET** /orders/pending
|
|
||||||
Devuelve todas las order que no hayan finalizado
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Pause
|
name: Pause
|
||||||
type: http
|
type: http
|
||||||
seq: 5
|
seq: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
@@ -15,7 +15,7 @@ params:query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125065160331
|
iccid: 8933201125065160414
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Preactivate
|
name: Preactivate
|
||||||
type: http
|
type: http
|
||||||
seq: 6
|
seq: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: ReActivate
|
|
||||||
type: http
|
|
||||||
seq: 12
|
|
||||||
}
|
|
||||||
|
|
||||||
post {
|
|
||||||
url: {{baseurl}}/sim/reActivate
|
|
||||||
body: formUrlEncoded
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
body:form-urlencoded {
|
|
||||||
iccid: 8935103196306448300
|
|
||||||
~offer: SAVEFAMILY1
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Select
|
|
||||||
type: http
|
|
||||||
seq: 13
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseurl}}/sim/select?iccid=8935103196306448300
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
iccid: 8935103196306448300
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Test Order
|
name: Test Order
|
||||||
type: http
|
type: http
|
||||||
seq: 10
|
seq: 9
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
docs {
|
docs {
|
||||||
Todos los endpoint tienen unos campos comunes de entrada:
|
Los endpoint tienen unos campos comunes de entrada:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
{
|
{
|
||||||
iccid: string,
|
iccid: string,
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
vars {
|
vars {
|
||||||
baseurl: http://localhost:3000
|
baseurl: http://localhost:3000
|
||||||
baseAlai: http://localhost:3002
|
|
||||||
}
|
}
|
||||||
color: #2E8A54
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
vars {
|
vars {
|
||||||
baseurl: https://sf-sims.savefamilygps.net
|
baseurl: https://sf-sims.savefamilygps.net
|
||||||
}
|
}
|
||||||
color: #CE4F3B
|
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
vars {
|
|
||||||
baseurl: http://sim-connections.savefamilygps.net
|
|
||||||
}
|
|
||||||
color: #C77A0F
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: test proxy
|
|
||||||
type: http
|
|
||||||
seq: 13
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseurl}}/simconnections/alai/select?iccid=1111111111111111111
|
|
||||||
body: none
|
|
||||||
auth: inherit
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
iccid: 1111111111111111111
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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,7 +0,0 @@
|
|||||||
name: local
|
|
||||||
color: "#2E8A54"
|
|
||||||
variables:
|
|
||||||
- name: baseurl
|
|
||||||
value: http://localhost:3001
|
|
||||||
- secret: true
|
|
||||||
name: token
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
name: prod
|
|
||||||
color: "#CE4F3B"
|
|
||||||
variables:
|
|
||||||
- name: baseurl
|
|
||||||
value: https://nosconnectcenter-api.iot-x.com
|
|
||||||
- secret: true
|
|
||||||
name: token
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
opencollection: 1.0.0
|
|
||||||
|
|
||||||
info:
|
|
||||||
name: sim-nos
|
|
||||||
bundled: false
|
|
||||||
extensions:
|
|
||||||
bruno:
|
|
||||||
ignore:
|
|
||||||
- node_modules
|
|
||||||
- .git
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: subscriber actions
|
|
||||||
type: http
|
|
||||||
seq: 1
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/subscribers/{{iccid}}/actions"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: iccid
|
|
||||||
value: "8935103196306448300"
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: subscriber info
|
|
||||||
type: http
|
|
||||||
seq: 2
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/subscribers/{{iccid}}"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: iccid
|
|
||||||
value: "8935103196306448300"
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: subscriber products available
|
|
||||||
type: http
|
|
||||||
seq: 4
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/subscribers/{{iccid}}/products/available"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: iccid
|
|
||||||
value: "8935103196306448300"
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
info:
|
|
||||||
name: subscribers
|
|
||||||
type: http
|
|
||||||
seq: 3
|
|
||||||
|
|
||||||
http:
|
|
||||||
method: GET
|
|
||||||
url: "{{baseurl}}/subscribers"
|
|
||||||
auth:
|
|
||||||
type: bearer
|
|
||||||
token: "{{token}}"
|
|
||||||
|
|
||||||
runtime:
|
|
||||||
variables:
|
|
||||||
- name: iccid
|
|
||||||
value: "8935103196306448300"
|
|
||||||
|
|
||||||
settings:
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
followRedirects: true
|
|
||||||
maxRedirects: 5
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Alarm by id
|
|
||||||
type: http
|
|
||||||
seq: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseUrl}}alarms/{{alarmId}}
|
|
||||||
body: none
|
|
||||||
auth: bearer
|
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{ws-access-token-partenaire}}
|
|
||||||
}
|
|
||||||
|
|
||||||
vars:pre-request {
|
|
||||||
alarmId: 2439
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Alerts
|
|
||||||
type: http
|
|
||||||
seq: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseUrl}}alarms/alerts?pageNumber=100
|
|
||||||
body: none
|
|
||||||
auth: bearer
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
pageNumber: 100
|
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{ws-access-token-partenaire}}
|
|
||||||
}
|
|
||||||
|
|
||||||
vars:pre-request {
|
|
||||||
alarmId: 2439
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: All Alarms
|
|
||||||
type: http
|
|
||||||
seq: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: {{baseUrl}}alarms
|
|
||||||
body: none
|
|
||||||
auth: bearer
|
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{ws-access-token-partenaire}}
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Alarms
|
|
||||||
seq: 21
|
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
mode: inherit
|
|
||||||
}
|
|
||||||
@@ -5,16 +5,16 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: https://api-getway.objenious.com/ws/lines?identifier.identifierType=ICCID&identifier.identifiers=8933201125065160455
|
url: https://api-getway.objenious.com/ws/lines?pageSize=1000&simStatus=ACTIVATED
|
||||||
body: formUrlEncoded
|
body: formUrlEncoded
|
||||||
auth: bearer
|
auth: bearer
|
||||||
}
|
}
|
||||||
|
|
||||||
params:query {
|
params:query {
|
||||||
identifier.identifierType: ICCID
|
pageSize: 1000
|
||||||
identifier.identifiers: 8933201125065160455
|
simStatus: ACTIVATED
|
||||||
~pageSize: 1000
|
~identifier.identifierType: ICCID
|
||||||
~simStatus: ACTIVATED
|
~identifier.identifiers: 8933201125065160455
|
||||||
}
|
}
|
||||||
|
|
||||||
auth:bearer {
|
auth:bearer {
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Consumption details
|
|
||||||
type: http
|
|
||||||
seq: 21
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: https://api-getway.objenious.com/ws/diagXL/massHistories
|
|
||||||
body: formUrlEncoded
|
|
||||||
auth: bearer
|
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{ws-access-token-partenaire}}
|
|
||||||
}
|
|
||||||
|
|
||||||
body:json {
|
|
||||||
{
|
|
||||||
"identifier": {
|
|
||||||
"identifiers": ["8933201124059175967"],
|
|
||||||
"identifierType": "ICCID"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body:form-urlencoded {
|
|
||||||
identifier.identifierType: "ICCID"
|
|
||||||
identifier.identifiers: ["8933201125068889373"]
|
|
||||||
}
|
|
||||||
|
|
||||||
vars:pre-request {
|
|
||||||
~id: 5187320
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
meta {
|
|
||||||
name: Line by iccid
|
|
||||||
type: http
|
|
||||||
seq: 22
|
|
||||||
}
|
|
||||||
|
|
||||||
get {
|
|
||||||
url: https://api-getway.objenious.com/ws/lines?pageSize=1000&simStatus=ACTIVATED
|
|
||||||
body: formUrlEncoded
|
|
||||||
auth: bearer
|
|
||||||
}
|
|
||||||
|
|
||||||
params:query {
|
|
||||||
pageSize: 1000
|
|
||||||
simStatus: ACTIVATED
|
|
||||||
~identifier.identifierType: ICCID
|
|
||||||
~identifier.identifiers: 8933201125065160455
|
|
||||||
}
|
|
||||||
|
|
||||||
auth:bearer {
|
|
||||||
token: {{ws-access-token-partenaire}}
|
|
||||||
}
|
|
||||||
|
|
||||||
body:json {
|
|
||||||
{
|
|
||||||
"identifier": {
|
|
||||||
"identifiers": ["8933201124059175967"],
|
|
||||||
"identifierType": "ICCID"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
body:form-urlencoded {
|
|
||||||
~identifier.identifierType: "ICCID"
|
|
||||||
~identifier.identifiers: ["8933201124059175967"]
|
|
||||||
}
|
|
||||||
|
|
||||||
settings {
|
|
||||||
encodeUrl: true
|
|
||||||
timeout: 0
|
|
||||||
}
|
|
||||||
@@ -5,13 +5,13 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{actionsUrl}}/massActions?massActionId=5363116
|
url: {{actionsUrl}}/massActions?massActionId=5192767
|
||||||
body: formUrlEncoded
|
body: formUrlEncoded
|
||||||
auth: bearer
|
auth: bearer
|
||||||
}
|
}
|
||||||
|
|
||||||
params:query {
|
params:query {
|
||||||
massActionId: 5363116
|
massActionId: 5192767
|
||||||
~identifier.identifierType: ICCID
|
~identifier.identifierType: ICCID
|
||||||
~identifier.identifiers: 8933201125065160463,8933201125065160422
|
~identifier.identifiers: 8933201125065160463,8933201125065160422
|
||||||
}
|
}
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -7,12 +7,11 @@
|
|||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest watch",
|
"test": "vitest watch",
|
||||||
"build": "rm -rf ./dist && yarn workspaces foreach -Api run build && yarn setup:runtime",
|
"build": "yarn workspaces foreach -A --exclude sim-consumidor-nos run build && cp .env dist/ && yarn setup:runtime",
|
||||||
"build:prod": "rm -rf ./dist && yarn workspaces foreach -Api run build:prod && yarn setup:runtime",
|
|
||||||
"setup:runtime": "mkdir -p dist/packages/node_modules && ln -sf ../sim-shared dist/packages/node_modules/sim-shared && ln -sf ../sf-consumidor-objenious dist/packages/node_modules/sim-consumidor-objenious",
|
"setup:runtime": "mkdir -p dist/packages/node_modules && ln -sf ../sim-shared dist/packages/node_modules/sim-shared && ln -sf ../sf-consumidor-objenious dist/packages/node_modules/sim-consumidor-objenious",
|
||||||
"start": "yarn workspaces foreach -Apiv run start",
|
"start": "yarn setup:runtime && yarn workspaces foreach -Apiv --exclude sim-consumidor-nos run start",
|
||||||
"typecheck": "npx tsc --noEmit",
|
"typecheck": "npx tsc --noEmit",
|
||||||
"dev": "yarn workspaces foreach -Apiv run dev",
|
"dev": "yarn workspaces foreach -Apiv --exclude sim-consumidor-nos run dev ",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"lint:fix": "eslint --fix .",
|
"lint:fix": "eslint --fix .",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
@@ -20,7 +19,7 @@
|
|||||||
"migrate": "yarn db-migrate -e .env -m deployment/database/migrations -t 99.0.0"
|
"migrate": "yarn db-migrate -e .env -m deployment/database/migrations -t 99.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sf-alvar/db-migrate": "1.0.6",
|
"@sf-alvar/db-migrate": "1.0.3",
|
||||||
"@tsconfig/node22": "^22.0.5",
|
"@tsconfig/node22": "^22.0.5",
|
||||||
"amqp-connection-manager": "^5.0.0",
|
"amqp-connection-manager": "^5.0.0",
|
||||||
"amqplib": "^0.10.9",
|
"amqplib": "^0.10.9",
|
||||||
@@ -29,7 +28,7 @@
|
|||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"pg": "^8.18.0",
|
"pg": "^8.18.0",
|
||||||
"typescript": "^6.0.3",
|
"typescript": "^5.9.3",
|
||||||
"uuidv7": "^1.1.0",
|
"uuidv7": "^1.1.0",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
"vite-tsconfig-paths": "^6.0.5"
|
"vite-tsconfig-paths": "^6.0.5"
|
||||||
|
|||||||
1
packages/_template/config/env/index.ts
vendored
1
packages/_template/config/env/index.ts
vendored
@@ -1,4 +1,5 @@
|
|||||||
export const env = {
|
export const env = {
|
||||||
|
ENVIRONMENT: process.env.ENVIORMENT,
|
||||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
POSTGRES_USER: process.env.POSTGRES_USER,
|
||||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
||||||
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
||||||
|
|||||||
1
packages/sim-alarmas-objenious/.env
Normal file
1
packages/sim-alarmas-objenious/.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PORT=3001
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Request, Response } from "express"
|
||||||
|
|
||||||
|
export class AlarmsController {
|
||||||
|
constructor() {
|
||||||
|
// Alarm usecases
|
||||||
|
}
|
||||||
|
|
||||||
|
public recibe() {
|
||||||
|
return (req: Request, res: Response) => {
|
||||||
|
const body = req.body()
|
||||||
|
const header = req.headers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
14
packages/sim-alarmas-objenious/aplication/Alarms.usecases.ts
Normal file
14
packages/sim-alarmas-objenious/aplication/Alarms.usecases.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
|
||||||
|
|
||||||
|
export class AlarmUsecases {
|
||||||
|
constructor(
|
||||||
|
private alarmRepository: any
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public saveAlarm() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +1,9 @@
|
|||||||
import { loadEnvFile } from "node:process";
|
import { loadEnvFile } from "node:process";
|
||||||
import path from "node:path";
|
loadEnvFile("../../.env")
|
||||||
|
|
||||||
try {
|
|
||||||
loadEnvFile(path.join("./.env")) // base
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error cargando el .env desde ./.env")
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
loadEnvFile(path.join("../../.env")) // Global
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error cargando el .env desde ../../.env")
|
|
||||||
}
|
|
||||||
|
|
||||||
export const env = {
|
export const env = {
|
||||||
|
PORT: Number(process.env.PORT),
|
||||||
|
ENVIRONMENT: process.env.ENVIORMENT,
|
||||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
POSTGRES_USER: process.env.POSTGRES_USER,
|
||||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
||||||
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
||||||
@@ -28,12 +19,5 @@ export const env = {
|
|||||||
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
||||||
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
||||||
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
||||||
|
|
||||||
APP_PORT: Number(process.env.NOS_PORT ?? 3001),
|
|
||||||
APP_HOST: String(process.env.NOS_HOST ?? "0.0.0.0"),
|
|
||||||
|
|
||||||
// ESPECIFICO NOS
|
|
||||||
NOS_BASE_URL: String(process.env.NOS_BASE_URL),
|
|
||||||
NOS_ACCESS_TOKEN: String(process.env.NOS_ACCESS_TOKEN)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Pool, QueryResult } from 'pg';
|
import { Pool } from 'pg';
|
||||||
import { PgClient } from 'sim-shared/infrastructure/PgClient.js'
|
import { PgClient } from 'sim-shared/infrastructure/PgClient.js'
|
||||||
import { env } from './env/env.js';
|
import { env } from './env/index.js';
|
||||||
|
|
||||||
// Configuracion de la conexion a la BDD, deberia ser la
|
// Configuracion de la conexion a la BDD, deberia ser la
|
||||||
// Misma para todos los servicios pero hasta que se unifique todo
|
// Misma para todos los servicios pero hasta que se unifique todo
|
||||||
@@ -10,9 +10,9 @@ export const pgPool = new Pool({
|
|||||||
host: env.POSTGRES_HOST,
|
host: env.POSTGRES_HOST,
|
||||||
database: env.POSTGRES_DATABASE,
|
database: env.POSTGRES_DATABASE,
|
||||||
password: env.POSTGRES_PASSWORD,
|
password: env.POSTGRES_PASSWORD,
|
||||||
port: Number(env.POSTGRES_PORT) || 5433,
|
port: Number(env.POSTGRES_PORT) || 5432,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const pgClient = new PgClient({
|
export const postgresClient = new PgClient({
|
||||||
pool: pgPool
|
pool: pgPool
|
||||||
})
|
})
|
||||||
62
packages/sim-alarmas-objenious/domain/alarmTypes.ts
Normal file
62
packages/sim-alarmas-objenious/domain/alarmTypes.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
export type ObjeniousAlarmType =
|
||||||
|
"PLMN_CHANGE" |
|
||||||
|
"COUNTRY_CHANGE" |
|
||||||
|
"OVER_CONSUMPTION_VOLUME" |
|
||||||
|
"UNDER_CONSUMPTION_VOLUME" |
|
||||||
|
"IMEI_CHANGE" |
|
||||||
|
"OVER_CONSUMPTION_VOLUME" |
|
||||||
|
string
|
||||||
|
|
||||||
|
export type ObjeniousAlarm = {
|
||||||
|
alarmId: Number,
|
||||||
|
emissionDate: Date //"07/02/2019 14:44:00"
|
||||||
|
alarmType: ObjeniousAlarmType,
|
||||||
|
lineInfos: {
|
||||||
|
msisdn: string,
|
||||||
|
iccid: string
|
||||||
|
},
|
||||||
|
change?: ObjeniousChangeTypes,
|
||||||
|
usage?: ObjeniousUsageTypes
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ObjeniousChangeTypes = ChangePayload | ImeiChangePayload;
|
||||||
|
export type ObjeniousUsageTypes = UsageAlarmPayload | OverConsumptionPayload;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PLMN_CHANGE
|
||||||
|
* COUNTRY_CHANGE
|
||||||
|
* STATUS_CHANGE
|
||||||
|
* */
|
||||||
|
export type ChangePayload = {
|
||||||
|
previous: string,
|
||||||
|
current: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMEI_CHANGE
|
||||||
|
*/
|
||||||
|
export type ImeiChangePayload = {
|
||||||
|
previous: string,//"42947662490276",
|
||||||
|
current: string, //"74845559130697",
|
||||||
|
previousManufacturer: string, //"u-blox AG",
|
||||||
|
currentManufacturer: string, //"SierraWireless"
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OVER_CONSUMPTION_VOLUME
|
||||||
|
* UNDER_CONSUMPTION_VOLUME
|
||||||
|
*/
|
||||||
|
export type UsageAlarmPayload = {
|
||||||
|
type: string,
|
||||||
|
threshold: string,
|
||||||
|
trigerValue: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OVER_CONSUMPTION_VOLUME
|
||||||
|
*/
|
||||||
|
export type OverConsumptionPayload = {
|
||||||
|
type: string, //"data-sms-smsIN-voiceOUT",
|
||||||
|
threshold: string, // "500-200-100-10",
|
||||||
|
triggerValue: string, // "600-300-101-15"
|
||||||
|
}
|
||||||
29
packages/sim-alarmas-objenious/index.ts
Normal file
29
packages/sim-alarmas-objenious/index.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import express from "express"
|
||||||
|
import cors from 'cors';
|
||||||
|
import { env } from "#config/env/index.js"
|
||||||
|
import { AlarmsController } from "aplication/Alarms.controller";
|
||||||
|
|
||||||
|
const PORT = env.PORT
|
||||||
|
const HOSTNAME = "0.0.0.0"
|
||||||
|
const app = express()
|
||||||
|
|
||||||
|
// Instancias
|
||||||
|
const alarmsController = new AlarmsController();
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
app.use("/fr/alarms", alarmsController.recibe())
|
||||||
|
|
||||||
|
app.get("/health", (req, res) => {
|
||||||
|
res.status(200).json({ status: "ok" })
|
||||||
|
})
|
||||||
|
|
||||||
|
app.listen(PORT, HOSTNAME, () => {
|
||||||
|
console.log("[o] Servidor iniciado en el puerto %d", PORT)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default {}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
|
||||||
|
|
||||||
|
export class AlarmsRepository {
|
||||||
|
constructor(private pgClient: PgClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public createAlarm() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
76
packages/sim-alarmas-objenious/package.json
Normal file
76
packages/sim-alarmas-objenious/package.json
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
{
|
||||||
|
"name": "sim-alarmas-objenious",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Recibe las alarmas de los webhook de objenious",
|
||||||
|
"main": "index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --import tsx --test ./**/*.test.ts",
|
||||||
|
"build": "tsc --build && tsc-alias -p tsconfig.json && cp package.json ../../dist/packages/sim-alarmas-objenious/",
|
||||||
|
"dev": "tsx watch index.ts",
|
||||||
|
"start": "node ../../dist/packages/sim-alarmas-objenious/index.js"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"packageManager": "yarn@4.12.0",
|
||||||
|
"imports": {
|
||||||
|
"#config/*.js": {
|
||||||
|
"types": "./config/*.ts",
|
||||||
|
"default": "./config/*.js"
|
||||||
|
},
|
||||||
|
"#config/*": {
|
||||||
|
"types": "./config/*.ts",
|
||||||
|
"default": "./config/*.js"
|
||||||
|
},
|
||||||
|
"#adapters/*.js": {
|
||||||
|
"types": "./adapters/*.ts",
|
||||||
|
"default": "./adapters/*.js"
|
||||||
|
},
|
||||||
|
"#adapters/*": {
|
||||||
|
"types": "./adapters/*.ts",
|
||||||
|
"default": "./adapters/*.js"
|
||||||
|
},
|
||||||
|
"#domain/*.js": {
|
||||||
|
"types": "./domain/*.ts",
|
||||||
|
"default": "./domain/*.js"
|
||||||
|
},
|
||||||
|
"#domain/*": {
|
||||||
|
"types": "./domain/*.ts",
|
||||||
|
"default": "./domain/*.js"
|
||||||
|
},
|
||||||
|
"#ports/*.js": {
|
||||||
|
"types": "./ports/*.ts",
|
||||||
|
"default": "./ports/*.js"
|
||||||
|
},
|
||||||
|
"#ports/*": {
|
||||||
|
"types": "./ports/*.ts",
|
||||||
|
"default": "./ports/*.js"
|
||||||
|
},
|
||||||
|
"#tests/*.js": {
|
||||||
|
"types": "./__tests__/*.ts",
|
||||||
|
"default": "./__tests__/*.js"
|
||||||
|
},
|
||||||
|
"#tests/*": {
|
||||||
|
"types": "./__tests__/*.ts",
|
||||||
|
"default": "./__tests__/*.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tsconfig/node22": "*",
|
||||||
|
"amqplib": "^0.10.9",
|
||||||
|
"cors": "*",
|
||||||
|
"dotenv": "*",
|
||||||
|
"express": "*",
|
||||||
|
"typescript": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/amqplib": "^0.10.8",
|
||||||
|
"@types/cors": "*",
|
||||||
|
"@types/express": "*",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/supertest": "*",
|
||||||
|
"prettier": "*",
|
||||||
|
"supertest": "*",
|
||||||
|
"tsx": "*",
|
||||||
|
"vitest": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
packages/sim-alarmas-objenious/tsconfig.json
Normal file
17
packages/sim-alarmas-objenious/tsconfig.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist",
|
||||||
|
"baseUrl": ".",
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"src/**/*.d.ts"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { AlaiRepository } from "#infrastructure/AlaiRepository.js";
|
|
||||||
import { JWTToken } from "sim-shared/domain/JWT.js";
|
|
||||||
import { JWTProvider } from "sim-shared/infrastructure/HTTPClient.js";
|
|
||||||
import { httpsAgent } from "#config/httpsAgent.js";
|
|
||||||
|
|
||||||
export class AlaiTokenManager implements JWTProvider<{}> {
|
|
||||||
|
|
||||||
isRefreshing: boolean = false;
|
|
||||||
authToken: JWTToken<{}> | undefined;
|
|
||||||
|
|
||||||
private async getNewAuthToken() {
|
|
||||||
// TODO: Si no funcionase hay que reprogramar los mensajes para ser
|
|
||||||
// consumidos mas tarde.
|
|
||||||
const res = await AlaiRepository.login(httpsAgent);
|
|
||||||
|
|
||||||
if (res.error != undefined) {
|
|
||||||
console.error("Error obteniendo el token de ALAI", res.error)
|
|
||||||
} else {
|
|
||||||
console.log("Obtenido token de ALAI: ", res)
|
|
||||||
this.authToken = new JWTToken(res.data.accessToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public tryRefreshToken(): Promise<JWTToken<{}>> {
|
|
||||||
// En Alai no existe el concepto de refresh, se solicita otro token nuevo
|
|
||||||
return this.getAccessToken()
|
|
||||||
};
|
|
||||||
|
|
||||||
public async getAccessToken(): Promise<JWTToken<{}>> {
|
|
||||||
// Caso 1: El token actual es valido
|
|
||||||
if (this.authToken != undefined && !this.authToken.isExpired()) {
|
|
||||||
return this.authToken
|
|
||||||
} else {
|
|
||||||
// Caso 2: El token actual no existe o ha expirado
|
|
||||||
await this.getNewAuthToken()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si después de todo no se ha generado el token es un error catastrofico
|
|
||||||
if (this.authToken == undefined) throw new Error("Error obteniendo tokens de auth")
|
|
||||||
|
|
||||||
return this.authToken
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
import { JWTToken } from "sim-shared/domain/JWT.js";
|
|
||||||
import { JWTProvider } from "sim-shared/infrastructure/HTTPClient.js";
|
|
||||||
import { LegacyJWTTokenRepository } from "#infrastructure/LegacyJWTTokensRepository.js";
|
|
||||||
import { env } from "#config/env/env.js";
|
|
||||||
|
|
||||||
const tokenDir = String(env.ALAI_CERTIFICATES_DIR)
|
|
||||||
const tokenFile = ".debugToken"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usa un token guardado a mano en archivo para no gastar tokens de Alai
|
|
||||||
*/
|
|
||||||
export class DebugTokenManager implements JWTProvider<{}> {
|
|
||||||
|
|
||||||
isRefreshing: boolean = false;
|
|
||||||
authToken: JWTToken<{}> | undefined;
|
|
||||||
|
|
||||||
private async getNewAuthToken() {
|
|
||||||
// TODO: Si no funcionase hay que reprogramar los mensajes para ser
|
|
||||||
// consumidos mas tarde.
|
|
||||||
const res = LegacyJWTTokenRepository.getTokenFromFile(tokenDir, tokenFile)
|
|
||||||
|
|
||||||
|
|
||||||
if (res.error != undefined) {
|
|
||||||
console.error("Error obteniendo el token de ALAI", res.error)
|
|
||||||
} else {
|
|
||||||
this.authToken = new JWTToken(res.data)
|
|
||||||
console.log("[d] Token DEBUG: ", this.authToken)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public tryRefreshToken(): Promise<JWTToken<{}>> {
|
|
||||||
// En Alai no existe el concepto de refresh, se solicita otro token nuevo
|
|
||||||
return this.getAccessToken()
|
|
||||||
};
|
|
||||||
|
|
||||||
public async getAccessToken(): Promise<JWTToken<{}>> {
|
|
||||||
// Caso 1: El token actual es valido
|
|
||||||
if (this.authToken != undefined && !this.authToken.isExpired()) {
|
|
||||||
return this.authToken
|
|
||||||
} else {
|
|
||||||
// Caso 2: El token actual no existe o ha expirado
|
|
||||||
await this.getNewAuthToken()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Si después de todo no se ha generado el token es un error catastrofico
|
|
||||||
if (this.authToken == undefined) throw new Error("Error obteniendo tokens de auth")
|
|
||||||
|
|
||||||
return this.authToken
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
import { ConsumeMessage } from "amqplib";
|
|
||||||
import { Request, Response } from "express"
|
|
||||||
import { SimAlaiUsecases } from "./SimAlai.usecases.js";
|
|
||||||
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 { iccidValidator } from "./httpValidators.js";
|
|
||||||
import { alaiSimToCommonSim } from "#domain/transformers.js";
|
|
||||||
|
|
||||||
export class SimAlaiController {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private uscases: SimAlaiUsecases,
|
|
||||||
private eventBus: EventBus,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateMsg(msg: ConsumeMessage | null) {
|
|
||||||
if (msg == undefined) return false;
|
|
||||||
const msgData = this.decodeMsg(msg) as SimEvents.general
|
|
||||||
if (msgData == undefined || msgData.payload == undefined) throw new Error("Mensaje invalido")
|
|
||||||
return msgData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private decodeMsg(msg: ConsumeMessage): object | undefined {
|
|
||||||
if (msg.content == undefined) {
|
|
||||||
console.warn('[Sim.controller] Mensaje vacío');
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Convertir el Buffer a String (UTF-8)
|
|
||||||
const contentJson = JSON.parse(Buffer.from(msg.content).toString('utf8'))
|
|
||||||
return contentJson;
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error al decodificar JSON:', error);
|
|
||||||
console.error(Buffer.from(msg.content).toString(("utf8")))
|
|
||||||
// Aquí podrías decidir devolver el string crudo o null
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Metodo duplicado se puede generalizar la a una clase sharedController con las funciones basicas
|
|
||||||
* TODO: meter un check de 429
|
|
||||||
*/
|
|
||||||
private async tryUseCase<T extends any>
|
|
||||||
(msg: ConsumeMessage, usecase: () => Promise<Result<string, T>>): Promise<Result<string, T>> {
|
|
||||||
try {
|
|
||||||
const result = await usecase()
|
|
||||||
if (result.error == undefined) {
|
|
||||||
await this.eventBus.ack(msg)
|
|
||||||
return result
|
|
||||||
} else {
|
|
||||||
console.error("Error procesando el caso de uso (Alai)", result.error)
|
|
||||||
this.eventBus.nack(msg)
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error general procesando el caso de uso (Alai)")
|
|
||||||
this.eventBus.nack(msg)
|
|
||||||
return {
|
|
||||||
error: String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public activate() {
|
|
||||||
return async (msg: ConsumeMessage) => {
|
|
||||||
console.log("[i] Evento activate ", msg.fields)
|
|
||||||
const data = this.validateMsg(msg) as SimEvents.activation
|
|
||||||
const iccid = data.payload.iccid
|
|
||||||
const correlation_id = data.headers?.message_id
|
|
||||||
const res = await this.tryUseCase(msg, this.uscases.activate({
|
|
||||||
iccid: iccid,
|
|
||||||
correlation_id: correlation_id
|
|
||||||
}))
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public suspend() {
|
|
||||||
return async (msg: ConsumeMessage) => {
|
|
||||||
console.log("Evento suspend ", msg.fields)
|
|
||||||
const data = this.validateMsg(msg) as SimEvents.suspend
|
|
||||||
const iccid = data.payload.iccid
|
|
||||||
const correlation_id = data.headers?.message_id
|
|
||||||
const res = await this.tryUseCase(msg, this.uscases.suspend({
|
|
||||||
iccid: iccid,
|
|
||||||
correlation_id: correlation_id
|
|
||||||
}))
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public reActivate() {
|
|
||||||
return async (msg: ConsumeMessage) => {
|
|
||||||
console.log("Evento reActivate ", msg.fields)
|
|
||||||
const data = this.validateMsg(msg) as SimEvents.reActivation
|
|
||||||
const iccid = data.payload.iccid
|
|
||||||
const correlation_id = data.headers?.message_id
|
|
||||||
const res = await this.tryUseCase(msg, this.uscases.reactivate({
|
|
||||||
iccid: iccid,
|
|
||||||
correlation_id: correlation_id
|
|
||||||
}))
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public terminate() {
|
|
||||||
return async (msg: ConsumeMessage) => {
|
|
||||||
console.log("Evento reActivate ", msg.fields)
|
|
||||||
const data = this.validateMsg(msg) as SimEvents.reActivation
|
|
||||||
const iccid = data.payload.iccid
|
|
||||||
const correlation_id = data.headers?.message_id
|
|
||||||
const res = await this.tryUseCase(msg, this.uscases.terminate({
|
|
||||||
iccid: iccid,
|
|
||||||
correlation_id: correlation_id
|
|
||||||
}))
|
|
||||||
|
|
||||||
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(422).json(validateBody)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const iccid: string | string[] = body.iccid
|
|
||||||
|
|
||||||
if (Array.isArray(iccid)) {
|
|
||||||
// TODO: Automatizar la paginacion
|
|
||||||
//const usecaseRes = this.uscases.selectMany({ iccid })
|
|
||||||
} else {
|
|
||||||
//const usecaseRes = await this.uscases.selectOne(iccid)
|
|
||||||
const usecaseRes = await this.uscases.selectCompleteSim(iccid)
|
|
||||||
if (usecaseRes.error != undefined) {
|
|
||||||
res.status(500).json(usecaseRes)
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
const { sim, subscription, imei } = usecaseRes.data
|
|
||||||
const simData = alaiSimToCommonSim(sim, subscription, imei)
|
|
||||||
res.send(simData)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json(validateBody)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
public selectPageREST() {
|
|
||||||
return async (req: Request, res: Response) => {
|
|
||||||
const { offset, limit, filter, orderBy } = req.query
|
|
||||||
const params = {
|
|
||||||
offset: (offset != undefined) ? Number(offset) : undefined,
|
|
||||||
limit: (limit != undefined) ? Number(limit) : undefined,
|
|
||||||
filter: (filter != undefined) ? String(filter) : undefined,
|
|
||||||
orderBy: (orderBy != undefined) ? String(orderBy) : undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const usecaseRes = await this.uscases.selectPage(params)
|
|
||||||
|
|
||||||
if (usecaseRes.error != undefined) {
|
|
||||||
res.status(500).json(usecaseRes)
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
res.status(200).send(usecaseRes.data)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
**/
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 { EventBus } from "sim-shared/domain/EventBus.port.js";
|
|
||||||
import { Result } from "sim-shared/domain/Result.js";
|
|
||||||
import { SimAlaiController } from "./SimAlai.controller.js";
|
|
||||||
|
|
||||||
type FuncType = ((m: ConsumeMessage) => Promise<Result<string, any>>)
|
|
||||||
|
|
||||||
export class SimAlaiRouter {
|
|
||||||
private readonly routes: Map<string, FuncType>;
|
|
||||||
|
|
||||||
// WIP
|
|
||||||
constructor(
|
|
||||||
private readonly simController: SimAlaiController,
|
|
||||||
private readonly eventBus: EventBus
|
|
||||||
) {
|
|
||||||
this.routes = new Map<string, FuncType>([
|
|
||||||
["activate", this.simController.activate()],
|
|
||||||
["pause", this.simController.suspend()],
|
|
||||||
["reactivate", this.simController.reActivate()],
|
|
||||||
["cancel", this.simController.terminate()],
|
|
||||||
["preActivate", this.simController.activate()]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 = handler(msg);
|
|
||||||
|
|
||||||
if (typeof executeParams === "function") {
|
|
||||||
const res = 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];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,264 +0,0 @@
|
|||||||
/**
|
|
||||||
* Documentación de referencia:
|
|
||||||
* https://pelion-help.iot-x.com/nos/en-US/Content/API/APIReference/API%20Reference.htm?tocpath=_____7
|
|
||||||
*
|
|
||||||
* En nos el correlation_id ya va a ser obligatorio en todos los mensajes
|
|
||||||
*
|
|
||||||
* TODO:
|
|
||||||
* - Control de errores más preciso
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import { AlaiAPI } from "#domain/AlaiAPI.js";
|
|
||||||
import { AlaiRepository } from "#infrastructure/AlaiRepository.js";
|
|
||||||
import { ConsumeMessage } from "amqplib";
|
|
||||||
import { ErrorOrderDTO, FinishOrderDTO, UpdateOrderDTO } from "sim-shared/domain/Order.js";
|
|
||||||
import { Result } from "sim-shared/domain/Result.js";
|
|
||||||
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js";
|
|
||||||
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js";
|
|
||||||
import { isOmittedExpression } from "typescript";
|
|
||||||
|
|
||||||
export class SimAlaiUsecases {
|
|
||||||
constructor(
|
|
||||||
private httpClient: HttpClient,
|
|
||||||
private alaiRepository: AlaiRepository,
|
|
||||||
private orderRepository: OrderRepository
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setRunning(correlation_id: string) {
|
|
||||||
const updateData: UpdateOrderDTO = {
|
|
||||||
new_status: "running",
|
|
||||||
correlation_id: correlation_id
|
|
||||||
}
|
|
||||||
const order = await this.orderRepository.updateOrder(updateData)
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setFinished(correlation_id: string) {
|
|
||||||
// En NOS el updateOrder se hace con el correlation_id que viene en la cabecera del
|
|
||||||
// mensaje consumido
|
|
||||||
const updateData: FinishOrderDTO = {
|
|
||||||
correlation_id: correlation_id
|
|
||||||
}
|
|
||||||
const order = await this.orderRepository.finishOrder(updateData)
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
|
|
||||||
private async setFailed(correlation_id: string, reason: string, detail?: string) {
|
|
||||||
// En NOS el updateOrder se hace con el correlation_id que viene en la cabecera del
|
|
||||||
// mensaje consumido
|
|
||||||
const updateData: ErrorOrderDTO = {
|
|
||||||
status: "failed",
|
|
||||||
correlation_id: correlation_id,
|
|
||||||
reason: reason,
|
|
||||||
error: reason,
|
|
||||||
stackTrace: detail
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("SET FAILED DATA:", updateData)
|
|
||||||
const order = await this.orderRepository.errorOrder(updateData)
|
|
||||||
console.log("SET FAILED RES:", order)
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gestiona el ciclo de vida de una petición. No aplica
|
|
||||||
* a peticiones de lectura (no pasan por la cola y no generan un order)
|
|
||||||
*/
|
|
||||||
public usecaseTemplate<T, R>(
|
|
||||||
func: (_: T) => Promise<Result<string, R>>,
|
|
||||||
args: T,
|
|
||||||
correlation_id?: string | undefined
|
|
||||||
) {
|
|
||||||
return async (): Promise<Result<string, R>> => {
|
|
||||||
// Operacion pending -> running
|
|
||||||
if (correlation_id != undefined)
|
|
||||||
this.setRunning(correlation_id)
|
|
||||||
.then()
|
|
||||||
.catch(e => console.error("Error actualizando el order", e))
|
|
||||||
else
|
|
||||||
console.warn("[!] Se ha lanzado una caso de uso sin correlation_id")
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await func(args)
|
|
||||||
|
|
||||||
if (res.error != undefined) {
|
|
||||||
console.log("Error peticion: ", res, correlation_id)
|
|
||||||
if (correlation_id != undefined)
|
|
||||||
this.setFailed(correlation_id, res.error)
|
|
||||||
.then(e => console.log("failed", e))
|
|
||||||
.catch(e => console.error(e))
|
|
||||||
return res;
|
|
||||||
} else {
|
|
||||||
if (correlation_id != undefined)
|
|
||||||
this.setFinished(correlation_id).then()
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
if (correlation_id != undefined)
|
|
||||||
this.setFailed(correlation_id, "Error general de operacion de SIM (NOS) ", String(e)).then()
|
|
||||||
return {
|
|
||||||
error: "Error general de operacion de SIM (NOS) " + String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public activate(args: {
|
|
||||||
iccid: string,
|
|
||||||
correlation_id: string | undefined
|
|
||||||
}) {
|
|
||||||
return this.usecaseTemplate(async (iccid /*iccid*/) => {
|
|
||||||
const order = await this.alaiRepository.createOrder()
|
|
||||||
if (order.error != undefined) {
|
|
||||||
// Falla el crearse un order (problema de servidor, token, etc)
|
|
||||||
console.error(order.error)
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
const reserved = await this.alaiRepository.createReserve(order.data.id, iccid)
|
|
||||||
return reserved
|
|
||||||
}, args.iccid, args.correlation_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
public preactivate(args: {
|
|
||||||
iccid: string,
|
|
||||||
correlation_id: string | undefined,
|
|
||||||
externalId: string | undefined // Por compatibilidad
|
|
||||||
}) {
|
|
||||||
const inputargs = {
|
|
||||||
iccid: args.iccid,
|
|
||||||
externalId: args.externalId
|
|
||||||
}
|
|
||||||
return this.usecaseTemplate(async (args) => {
|
|
||||||
const order = await this.alaiRepository.createOrder()
|
|
||||||
if (order.error != undefined) {
|
|
||||||
return order
|
|
||||||
}
|
|
||||||
const orderId = order.data.id
|
|
||||||
const reserve = await this.alaiRepository.createReserve(orderId, args.iccid)
|
|
||||||
if (reserve.error != undefined) {
|
|
||||||
return reserve
|
|
||||||
}
|
|
||||||
|
|
||||||
const applyOrder = await this.alaiRepository.applyOrder(orderId)
|
|
||||||
if (applyOrder.error != undefined) {
|
|
||||||
// TODO: gestion del error
|
|
||||||
// reusar el orderId
|
|
||||||
return applyOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
const preactivatedSim = await this.alaiRepository.getSimByICCID(args.iccid)
|
|
||||||
if (preactivatedSim.error != undefined) {
|
|
||||||
return preactivatedSim
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Controlar sim no encotrada (No deberia pasar)
|
|
||||||
const subscriptionId = preactivatedSim.data!.subscription.id
|
|
||||||
if (args.externalId) {
|
|
||||||
const externalIdAdded = await this.alaiRepository.changeExternalId(subscriptionId, args.externalId)
|
|
||||||
if (externalIdAdded.error != undefined) {
|
|
||||||
return externalIdAdded
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// En connections acaba buscando el numero.
|
|
||||||
const subscription = await this.alaiRepository.getSubscriptionById(subscriptionId)
|
|
||||||
return subscription
|
|
||||||
}, inputargs, args.correlation_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
public suspend(args: {
|
|
||||||
iccid: string,
|
|
||||||
correlation_id: string | undefined
|
|
||||||
}) {
|
|
||||||
return this.usecaseTemplate(async (args) => {
|
|
||||||
const subscription = await this.alaiRepository.getSimByICCID(args.iccid)
|
|
||||||
if (subscription.error != undefined) {
|
|
||||||
return subscription
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Controlar que no se encuentre la subscription
|
|
||||||
const suspension = this.alaiRepository.pauseSubscription(subscription.data!.id)
|
|
||||||
return suspension
|
|
||||||
}, args, args.correlation_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public reactivate(args: {
|
|
||||||
iccid: string,
|
|
||||||
correlation_id: string | undefined
|
|
||||||
}) {
|
|
||||||
return this.usecaseTemplate(async (args) => {
|
|
||||||
const subscription = await this.alaiRepository.getSimByICCID(args.iccid)
|
|
||||||
if (subscription.error != undefined) {
|
|
||||||
return subscription
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Controlar que no se encuentre la subscription
|
|
||||||
const suspension = this.alaiRepository.unPauseSubscription(subscription.data!.id)
|
|
||||||
return suspension
|
|
||||||
}, args, args.correlation_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public terminate(args: {
|
|
||||||
iccid: string,
|
|
||||||
correlation_id: string | undefined
|
|
||||||
}) {
|
|
||||||
return this.usecaseTemplate(async (args) => {
|
|
||||||
const subscription = await this.alaiRepository.getSimByICCID(args.iccid)
|
|
||||||
if (subscription.error != undefined) {
|
|
||||||
return subscription
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Controlar que no se encuentre la subscription
|
|
||||||
const suspension = this.alaiRepository.terminateSubscription(subscription.data!.id)
|
|
||||||
return suspension
|
|
||||||
}, args, args.correlation_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async selectOne(iccid: string) {
|
|
||||||
const sim = await this.alaiRepository.getSimByICCID(iccid)
|
|
||||||
return sim
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Para sacar los datos de una liena hay que sacar sim -> subscripcion -> imei
|
|
||||||
* son 3 llamadas distintas.
|
|
||||||
*/
|
|
||||||
public async selectCompleteSim(iccid: string): Promise<Result<string, {
|
|
||||||
sim: AlaiAPI.Sim,
|
|
||||||
subscription: AlaiAPI.Subscription,
|
|
||||||
imei: AlaiAPI.GetImeiSubscriptionDTO
|
|
||||||
}>> {
|
|
||||||
const sim = await this.alaiRepository.getSimByICCID(iccid)
|
|
||||||
|
|
||||||
if (sim.error != undefined) {
|
|
||||||
return sim
|
|
||||||
}
|
|
||||||
|
|
||||||
const subscriptionId = sim.data!.subscription.id
|
|
||||||
const subscription = await this.alaiRepository.getSubscriptionById(subscriptionId)
|
|
||||||
|
|
||||||
if (subscription.error != undefined) {
|
|
||||||
return subscription
|
|
||||||
}
|
|
||||||
|
|
||||||
const imei = await this.alaiRepository.getImeiFromSubscription(subscriptionId)
|
|
||||||
if (imei.error != undefined) {
|
|
||||||
return imei
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
sim: sim.data!,
|
|
||||||
subscription: subscription.data!,
|
|
||||||
imei: imei.data!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import fs from "fs";
|
|
||||||
import path from "path";
|
|
||||||
import { Result } from "sim-shared/domain/Result.js";
|
|
||||||
|
|
||||||
export type P12Cert = {
|
|
||||||
cainfo: string,
|
|
||||||
p12cert: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SSLCert = {
|
|
||||||
cainfo: string,
|
|
||||||
sslcert: string,
|
|
||||||
keypem: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO:
|
|
||||||
* - Se ha usado https.Agent en su lugar, eliminar si no se usa
|
|
||||||
*/
|
|
||||||
export class SSLCertificateLoader {
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private certificatesDir: string,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadCertificatesP12(caFile: string, certFile: string): Result<string, P12Cert> {
|
|
||||||
try {
|
|
||||||
const cainfo = fs.readFileSync(path.resolve(this.certificatesDir, caFile)).toString();
|
|
||||||
const p12cert = fs.readFileSync(path.resolve(this.certificatesDir, certFile)).toString();
|
|
||||||
return { data: { cainfo, p12cert } };
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[x] Error cargando los certificados P12", e)
|
|
||||||
return {
|
|
||||||
error: String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public loadCertificatesSSL(caFile: string, certFile: string, keyFile: string): Result<string, SSLCert> {
|
|
||||||
try {
|
|
||||||
const cainfo = fs.readFileSync(path.resolve(this.certificatesDir, caFile)).toString();
|
|
||||||
const sslcert = fs.readFileSync(path.resolve(this.certificatesDir, certFile), { encoding: null }).toString();
|
|
||||||
const keypem = fs.readFileSync(path.resolve(this.certificatesDir, keyFile), { encoding: null }).toString();
|
|
||||||
return { data: { cainfo, sslcert, keypem } };
|
|
||||||
} catch (e) {
|
|
||||||
console.error("[x] Error cargando los certificados SSL", e)
|
|
||||||
return {
|
|
||||||
error: String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
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,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
eyJhbGciOiJIUzM4NCJ9.eyJiciI6InNhdmVmYW1pbHkiLCJpcCI6Ijg4LjE1LjE1Ny4xNjciLCJzdWIiOiJwYWxvbWFpYmFuZXoiLCJzIjoiRVdTMTY0NmFmNjNlZGUyMjgzIiwicG9zIjoic2F2ZWZhbWlseUNhYyIsImlkV3NVc2VyIjoiODYiLCJpc012bmEiOmZhbHNlLCJkb21haW4iOiJBbGFpfHNhdmVmYW1pbHkiLCJpYXQiOjE3Nzc4OTk3MzcsImV4cCI6MTc3NzkxMDUzN30.PvTTRhUpKlslGOerQsLY4RLBXdQ5FIVvUKb_1ZK4b2Zggt04KZhwX0d-XoLAcP93
|
|
||||||
Binary file not shown.
61
packages/sim-consumidor-alai/config/env/env.ts
vendored
61
packages/sim-consumidor-alai/config/env/env.ts
vendored
@@ -1,61 +0,0 @@
|
|||||||
import { loadEnvFile } from "node:process";
|
|
||||||
import path from "node:path";
|
|
||||||
import assert from "node:assert";
|
|
||||||
|
|
||||||
try {
|
|
||||||
loadEnvFile(path.join("../../.env")) // Global
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error cargando el .env desde ../../.env")
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
loadEnvFile(path.join("./.env")) // base
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error cargando el .env desde ./.env")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const env = {
|
|
||||||
ENVIRONMENT: process.env.ENVIORMENT,
|
|
||||||
POSTGRES_USER: process.env.POSTGRES_USER,
|
|
||||||
POSTGRES_PASSWORD: process.env.POSTGRES_PASSWORD,
|
|
||||||
POSTGRES_PORT: process.env.POSTGRES_PORT,
|
|
||||||
POSTGRES_HOST: process.env.POSTGRES_HOST,
|
|
||||||
POSTGRES_DATABASE: process.env.POSTGRES_DATABASE,
|
|
||||||
RABBITMQ_HOST: String(process.env.RABBITMQ_HOST ?? "localhost"),
|
|
||||||
RABBITMQ_USER: String(process.env.RABBITMQ_USER ?? "test"),
|
|
||||||
RABBITMQ_PASSWORD: String(process.env.RABBITMQ_PASSWORD ?? "test"),
|
|
||||||
RABBITMQ_EXCHANGE: String(process.env.RABBITMQ_EXCHANGE ?? "/"),
|
|
||||||
RABBITMQ_PORT: parseInt(process.env.RABBITMQ_PORT ?? "5672"),
|
|
||||||
RABBITMQ_MODULENAME: process.env.MODULENAME,
|
|
||||||
RABBITMQ_TTL: process.env.RABBITMQ_TTL,
|
|
||||||
RABBITMQ_SECURE: process.env.RABBITMQ_SECURE,
|
|
||||||
RABBITMQ_RETRY_INTERVAL: process.env.RABBITMQ_INTERVAL,
|
|
||||||
RABBITMQ_VHOST: String(process.env.RABBITMQ_VHOST),
|
|
||||||
|
|
||||||
ALAI_PORT: parseInt(process.env.ALAI_PORT ?? "3002"),
|
|
||||||
ALAI_HOST: String(process.env.ALAI_HOST),
|
|
||||||
|
|
||||||
// ESPECIFICO ALAI
|
|
||||||
ALAI_API_URL: process.env.ALAI_API_URL,
|
|
||||||
ALAI_CERTIFICATES_DIR: process.env.ALAI_CERTIFICATES_DIR,
|
|
||||||
ALAI_CERTIFICATE_NAME: process.env.ALAI_CERTIFICATE_NAME,
|
|
||||||
ALAI_CERTIFICATE_PASSWORD: process.env.ALAI_CERTIFICATE_PASSWORD,
|
|
||||||
ALAI_USERNAME: process.env.ALAI_USERNAME,
|
|
||||||
ALAI_PASSWORD: process.env.ALAI_PASSWORD,
|
|
||||||
ALAI_BRANDID: process.env.ALAI_BRANDID,
|
|
||||||
|
|
||||||
ALAI_PACKAGE: process.env.ALAI_PACKAGE,
|
|
||||||
ALAI_SUBSCRIBER_ID: process.env.ALAI_SUBSCRIBER_ID
|
|
||||||
};
|
|
||||||
|
|
||||||
assert.ok(env.ALAI_SUBSCRIBER_ID != undefined, "ALAI_SUBSCRIBER_ID no definido")
|
|
||||||
assert.ok(env.ALAI_PACKAGE != undefined, "ALAI_PACKAGE no definido")
|
|
||||||
assert.ok(env.ALAI_USERNAME != undefined, "ALAI_USERNAME no definido")
|
|
||||||
assert.ok(env.ALAI_PASSWORD != undefined, "ALAI_PASSWORD no definido")
|
|
||||||
assert.ok(env.ALAI_BRANDID != undefined, "ALAI_BRANDID no definido")
|
|
||||||
assert.ok(env.ALAI_API_URL != undefined, "ALAI_API_URL no definido")
|
|
||||||
|
|
||||||
assert.ok(env.ALAI_CERTIFICATE_NAME != undefined, "ALAI_CERTIFICATE_NAME no definido")
|
|
||||||
assert.ok(env.ALAI_CERTIFICATES_DIR != undefined, "ALAI_CERTIFICATES_DIR no definido")
|
|
||||||
assert.ok(env.ALAI_CERTIFICATE_PASSWORD != undefined, "ALAI_CERTIFICATE_PASSWORD no definido")
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import { RabbitMQEventBus, RMQConnectionParams } from "sim-shared/infrastructure/RabbitMQEventBus.js"
|
|
||||||
import { Channel } from "amqp-connection-manager"
|
|
||||||
import { env } from "./env/env.js"
|
|
||||||
|
|
||||||
const rmqUser = env.RABBITMQ_USER
|
|
||||||
const rmqPass = env.RABBITMQ_PASSWORD
|
|
||||||
const rmqHost = env.RABBITMQ_HOST
|
|
||||||
const rmqPort = Number(env.RABBITMQ_PORT)
|
|
||||||
const rmqSecure = false
|
|
||||||
const rmqVhost = env.RABBITMQ_VHOST
|
|
||||||
|
|
||||||
export const rmqConnOptions = <RMQConnectionParams>{
|
|
||||||
username: rmqUser,
|
|
||||||
password: rmqPass,
|
|
||||||
vhost: rmqVhost,
|
|
||||||
hostname: rmqHost,
|
|
||||||
port: rmqPort,
|
|
||||||
secure: rmqSecure,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const BASE_ALAI_KEY = "sim.alai.#"
|
|
||||||
const QUEUES = {
|
|
||||||
MAIN: "sim.alai",
|
|
||||||
DLX: "sim.alai.dlx",
|
|
||||||
DELAY: "sim.alai.delayed",
|
|
||||||
}
|
|
||||||
|
|
||||||
const EXCHANGES = {
|
|
||||||
MAIN: "sim.exchange",
|
|
||||||
DLX: "sim.ex.alai.dlx",
|
|
||||||
DEL: "sim.ex.alai.delayed"
|
|
||||||
}
|
|
||||||
|
|
||||||
export const rabbitmqEventBus = new RabbitMQEventBus({
|
|
||||||
connectionParams: rmqConnOptions,
|
|
||||||
buildStructure: buildQueues,
|
|
||||||
maxRetry: 2,
|
|
||||||
delayedExchange: EXCHANGES.DEL,
|
|
||||||
dlxExchange: EXCHANGES.DLX
|
|
||||||
})
|
|
||||||
|
|
||||||
async function buildQueues(channel: Channel) {
|
|
||||||
|
|
||||||
const DELAY = 10 * 1000
|
|
||||||
|
|
||||||
await channel.assertExchange(EXCHANGES.DEL, "topic")
|
|
||||||
await channel.assertExchange(EXCHANGES.DLX, "topic")
|
|
||||||
await channel.assertExchange(EXCHANGES.MAIN, "topic")
|
|
||||||
|
|
||||||
await channel.assertQueue(QUEUES.MAIN)
|
|
||||||
await channel.assertQueue(QUEUES.DLX)
|
|
||||||
await channel.assertQueue(QUEUES.DELAY, {
|
|
||||||
durable: true,
|
|
||||||
arguments: {
|
|
||||||
'x-message-ttl': DELAY,
|
|
||||||
'x-dead-letter-exchange': EXCHANGES.MAIN,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Cola dead-letter
|
|
||||||
await channel.bindQueue(QUEUES.DLX, EXCHANGES.DLX, BASE_ALAI_KEY)
|
|
||||||
// Cola delay
|
|
||||||
await channel.bindQueue(QUEUES.DELAY, EXCHANGES.DEL, BASE_ALAI_KEY)
|
|
||||||
// Cola nos -> main exchange
|
|
||||||
await channel.bindQueue(QUEUES.MAIN, EXCHANGES.MAIN, BASE_ALAI_KEY)
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function startRMQClient() {
|
|
||||||
await rabbitmqEventBus.connect()
|
|
||||||
return rabbitmqEventBus
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"
|
|
||||||
import { AlaiTokenManager } from "#aplication/AlaiTokenManager.js"
|
|
||||||
import { env } from "#config/env/env.js";
|
|
||||||
import { httpsAgent } from "./httpsAgent.js"
|
|
||||||
import { DebugTokenManager } from "#aplication/DebugTokenManager.js";
|
|
||||||
|
|
||||||
const tokenManager = new AlaiTokenManager()
|
|
||||||
//const debugTokenManagr = new DebugTokenManager()
|
|
||||||
//console.error("USANDO DebugTokenManager! Eliminar en prod")
|
|
||||||
|
|
||||||
|
|
||||||
export const alaiHttp = new HttpClient({
|
|
||||||
baseURL: env.ALAI_API_URL as string,
|
|
||||||
headers: {},
|
|
||||||
jwtManager: tokenManager,
|
|
||||||
httpsAgent: httpsAgent
|
|
||||||
})
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import https from 'https';
|
|
||||||
import { env } from './env/env.js';
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
const certificatesDir = String(env.ALAI_CERTIFICATES_DIR)
|
|
||||||
const certificateName = String(env.ALAI_CERTIFICATE_NAME)
|
|
||||||
const certificatePassword = String(env.ALAI_CERTIFICATE_PASSWORD)
|
|
||||||
|
|
||||||
export const httpsAgent = new https.Agent({
|
|
||||||
pfx: fs.readFileSync(path.join(certificatesDir, certificateName)),
|
|
||||||
passphrase: certificatePassword
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,294 +0,0 @@
|
|||||||
import { StringMappingType } from "typescript"
|
|
||||||
|
|
||||||
export namespace AlaiAPI {
|
|
||||||
|
|
||||||
export type LoginResponseDTO = {
|
|
||||||
accessToken: string,
|
|
||||||
tokenType: string,
|
|
||||||
refreshToken: string,
|
|
||||||
expiresIn: string // isodate
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
Hardcodeado en:
|
|
||||||
sf-sim-connections/context/infrastructure/api/alaiService.js
|
|
||||||
const data = {
|
|
||||||
type: "RETAIL",
|
|
||||||
salesChannel: "OWN_CALLCENTER",
|
|
||||||
status: "CONFIRMED",
|
|
||||||
packages: [{ id: "Tarifa_250MB_100MIN_5SMS" }],
|
|
||||||
subscriber: { id: "16216" }
|
|
||||||
};
|
|
||||||
*/
|
|
||||||
export type CreateOrderDTO = {
|
|
||||||
type: "RETAIL" | string,
|
|
||||||
salesChannel: "OWN_CALLCENTER" | string,
|
|
||||||
status: "CONFIRMED" | string,
|
|
||||||
packages: { id: "Tarifa_250MB_100MIN_5SMS" | string }[],
|
|
||||||
subscriber: {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OrderPackage = {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
packagePrices: unknown,
|
|
||||||
packageInstance: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Link = {
|
|
||||||
rel: string,
|
|
||||||
href: string,
|
|
||||||
hreflang: string,
|
|
||||||
media: string,
|
|
||||||
title: string,
|
|
||||||
type: string,
|
|
||||||
deprecation: string,
|
|
||||||
profile: string,
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type UpdateSubscriptionDTO = {
|
|
||||||
location: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ApplyOrderDTO = UpdateSubscriptionDTO
|
|
||||||
|
|
||||||
export type Subscription = {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
domain: string,
|
|
||||||
status: Status,
|
|
||||||
networkStatus: NetworkStatus,
|
|
||||||
type: "RETAIL" | string,
|
|
||||||
portabilityStatus: "NO_PORTABILITY" | string,
|
|
||||||
billingType: string,
|
|
||||||
creationDate: string, // ISODATE
|
|
||||||
firstActivationDate: string, // ISODATE
|
|
||||||
terminationDate: string, // ISODATE
|
|
||||||
balance: number,
|
|
||||||
balanceExpirationDate: string, // ISODATE
|
|
||||||
lastTrafficDate: string, // ISODATE
|
|
||||||
externalName: string,
|
|
||||||
language: string,
|
|
||||||
ntwID: string,
|
|
||||||
publicIdentity: string,
|
|
||||||
externalID: string,
|
|
||||||
lastMsisdnID: string,
|
|
||||||
msisdn: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
lastIccID: string,
|
|
||||||
priceplan: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
pricePlanName: string
|
|
||||||
},
|
|
||||||
salesData: {
|
|
||||||
salesChannel: string,
|
|
||||||
salesPerson: string,
|
|
||||||
},
|
|
||||||
address: {
|
|
||||||
country: string,
|
|
||||||
state: string,
|
|
||||||
county: string,
|
|
||||||
city: string,
|
|
||||||
street: string,
|
|
||||||
postalCode: string,
|
|
||||||
number: string,
|
|
||||||
description: string,
|
|
||||||
neighborhood: string,
|
|
||||||
typeSettlement: string,
|
|
||||||
normalized: boolean,
|
|
||||||
externalID: string,
|
|
||||||
externalType: string,
|
|
||||||
spainSpecial: {
|
|
||||||
externalRefList:
|
|
||||||
{
|
|
||||||
refId: string,
|
|
||||||
refType: string
|
|
||||||
}[],
|
|
||||||
streetType: number,
|
|
||||||
ineCityCode: string,
|
|
||||||
ineSingularEntityCode: string,
|
|
||||||
floor: string,
|
|
||||||
door: string,
|
|
||||||
apartmentNumber: string,
|
|
||||||
staircaseNumber: string,
|
|
||||||
streetNrLast: string,
|
|
||||||
streetNrLastSuffix: string,
|
|
||||||
subUnitNumber: string,
|
|
||||||
buildingName: string,
|
|
||||||
homeID: string
|
|
||||||
},
|
|
||||||
iranSpecial: unknown,
|
|
||||||
mexicoSpecial: unknown,
|
|
||||||
brazilSpecial: unknown,
|
|
||||||
},
|
|
||||||
msisdnList: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}[],
|
|
||||||
terminalList: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}[]
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CreateOrderResponseDTO = {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
domain: string,
|
|
||||||
orderCode: string,
|
|
||||||
externalID: string,
|
|
||||||
type: string,
|
|
||||||
status: string,
|
|
||||||
saleStatus: string,
|
|
||||||
distributionStatus: string,
|
|
||||||
description: string,
|
|
||||||
salesChannel: string,
|
|
||||||
salesPerson: string,
|
|
||||||
deliveryType: string,
|
|
||||||
distributionInfo: {
|
|
||||||
providerID: string,
|
|
||||||
providerReference: string,
|
|
||||||
providerTracking: string,
|
|
||||||
cashOnDelivery: boolean,
|
|
||||||
prepaidShipping: boolean,
|
|
||||||
description: string,
|
|
||||||
events: {
|
|
||||||
status: string,
|
|
||||||
observations: string,
|
|
||||||
date: string | Date,
|
|
||||||
expectedDeliveryDate: string | Date,
|
|
||||||
completedDeliveryDate: string | Date,
|
|
||||||
}[]
|
|
||||||
},
|
|
||||||
packages: OrderPackage[],
|
|
||||||
subscription: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
subscriber: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
brand: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
pos: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NetworkStatus =
|
|
||||||
"ACTIVE" |
|
|
||||||
"BLOCKED" |
|
|
||||||
"DEACTIVATE" |
|
|
||||||
"FRAUD" |
|
|
||||||
"PRE_ACTIVE"
|
|
||||||
|
|
||||||
export type Status =
|
|
||||||
"ABORTED" |
|
|
||||||
"ACTIVE" |
|
|
||||||
"BLOCKEDCORE" |
|
|
||||||
"BLOCKEDFRAUD" |
|
|
||||||
"CANCELLED" |
|
|
||||||
"CONFIGURING" |
|
|
||||||
"DELETED" |
|
|
||||||
"PRE_ACTIVE" |
|
|
||||||
"TERMINATED"
|
|
||||||
|
|
||||||
export type Sim = {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
simCode: string,
|
|
||||||
puk: string,
|
|
||||||
puk2: string,
|
|
||||||
pin: string,
|
|
||||||
pin2: string,
|
|
||||||
status: string,
|
|
||||||
storeStatus: string,
|
|
||||||
statusEsim: string,
|
|
||||||
pool: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
sourcePool: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
subscription: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
imsi: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
msisdn: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
distributedPos: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
pkgi: {
|
|
||||||
id: string,
|
|
||||||
name: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
links: Link[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GetImeiSubscriptionDTO = {
|
|
||||||
links: Link[],
|
|
||||||
content: {
|
|
||||||
id: string,
|
|
||||||
sim: {
|
|
||||||
id: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
imsi: string,
|
|
||||||
lastChange: string, //ISODATE
|
|
||||||
lastUpdate: string, //ISODATE
|
|
||||||
model: string,
|
|
||||||
subscription: {
|
|
||||||
id: string,
|
|
||||||
links: Link[]
|
|
||||||
},
|
|
||||||
links: Link[]
|
|
||||||
}[],
|
|
||||||
page: {
|
|
||||||
size: number,
|
|
||||||
totalElements: number,
|
|
||||||
totalPages: number,
|
|
||||||
number: number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { Result } from "sim-shared/domain/Result.js";
|
|
||||||
import { AlaiAPI } from "./AlaiAPI.js";
|
|
||||||
import { CommonSim } from "sim-shared/domain/CommonSim.js";
|
|
||||||
|
|
||||||
const alaiStates = new Map<AlaiAPI.Status, CommonSim<any>["billing_status"]>([
|
|
||||||
["ABORTED", "SUSPENDED"],
|
|
||||||
["ACTIVE", "ACTIVE"],
|
|
||||||
["BLOCKEDCORE", "SUSPENDED"],
|
|
||||||
["BLOCKEDFRAUD", "SUSPENDED"],
|
|
||||||
["CANCELLED", "TERMINATED"],
|
|
||||||
["CONFIGURING", "SUSPENDED"],
|
|
||||||
["DELETED", "TERMINATED"],
|
|
||||||
["PRE_ACTIVE", "PREACTIVATED"],
|
|
||||||
["TERMINATED", "TERMINATED"]
|
|
||||||
])
|
|
||||||
|
|
||||||
const alaiNetworkStates = new Map<AlaiAPI.NetworkStatus, CommonSim<any>["network_status"]>([
|
|
||||||
["ACTIVE", "ACTIVE"],
|
|
||||||
["PRE_ACTIVE", "PREACTIVATED"],
|
|
||||||
["BLOCKED", "SUSPENDED"],
|
|
||||||
["DEACTIVATE", "SUSPENDED"],
|
|
||||||
["FRAUD", "TERMINATED"]
|
|
||||||
])
|
|
||||||
|
|
||||||
export function alaiSimToCommonSim(alaiSim: AlaiAPI.Sim, alaiSubscription: AlaiAPI.Subscription, imeiSubscription: AlaiAPI.GetImeiSubscriptionDTO):
|
|
||||||
Result<string, CommonSim<AlaiAPI.Subscription>> {
|
|
||||||
|
|
||||||
const status = alaiStates.get(alaiSubscription.status) ?? "UNKNOWN"
|
|
||||||
const networkStatus = alaiNetworkStates.get(alaiSubscription.networkStatus) ?? "UNKNOWN"
|
|
||||||
|
|
||||||
const commonSim: CommonSim<AlaiAPI.Subscription> = {
|
|
||||||
company: "ALAI",
|
|
||||||
tariff: alaiSubscription.name,
|
|
||||||
iccid: alaiSim.id,
|
|
||||||
msisdn: alaiSubscription.lastMsisdnID,
|
|
||||||
billing_status: status,
|
|
||||||
network_status: networkStatus,
|
|
||||||
raw: alaiSubscription,
|
|
||||||
imei: imeiSubscription.content[0].id ?? "0",
|
|
||||||
preactivation_date: new Date(alaiSubscription.creationDate),
|
|
||||||
activation_date: new Date(alaiSubscription.firstActivationDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: commonSim
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import express from "express"
|
|
||||||
import cors from 'cors';
|
|
||||||
import { env } from "#config/env/env.js"
|
|
||||||
import { startRMQClient } from "#config/eventBus.config.js";
|
|
||||||
import { SimAlaiRouter } from "#aplication/SimAlai.router.js";
|
|
||||||
import { SimAlaiController } from "#aplication/SimAlai.controller.js";
|
|
||||||
import { SimAlaiUsecases } from "#aplication/SimAlai.usecases.js";
|
|
||||||
import { alaiHttp } from "#config/httpClient.config.js";
|
|
||||||
import { AlaiRepository } from "#infrastructure/AlaiRepository.js";
|
|
||||||
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js";
|
|
||||||
import { pgClient } from "#config/postgreConfig.js";
|
|
||||||
|
|
||||||
const RMQ_QUEUE = "sim.alai"
|
|
||||||
const PORT = env.ALAI_PORT
|
|
||||||
const HOSTNAME = env.ALAI_HOST
|
|
||||||
|
|
||||||
async function startWorker() {
|
|
||||||
// Instancia de dependencias
|
|
||||||
|
|
||||||
const rmqClient = await startRMQClient()
|
|
||||||
|
|
||||||
const orderRepository = new OrderRepository(pgClient)
|
|
||||||
|
|
||||||
const alaiRepository = new AlaiRepository(alaiHttp)
|
|
||||||
|
|
||||||
const alaiUsecases = new SimAlaiUsecases(
|
|
||||||
alaiHttp,
|
|
||||||
alaiRepository,
|
|
||||||
orderRepository
|
|
||||||
)
|
|
||||||
|
|
||||||
const alaiController = new SimAlaiController(
|
|
||||||
alaiUsecases,
|
|
||||||
rmqClient
|
|
||||||
)
|
|
||||||
|
|
||||||
const simRouter = new SimAlaiRouter(
|
|
||||||
alaiController,
|
|
||||||
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 }));
|
|
||||||
|
|
||||||
// WIP
|
|
||||||
app.get("/select", alaiController.selectREST())
|
|
||||||
app.get("/health",
|
|
||||||
(req, res) => res.json({
|
|
||||||
ok: "alai"
|
|
||||||
}))
|
|
||||||
//app.get("/selectPage", alaiController.selectPageREST())
|
|
||||||
|
|
||||||
app.listen(PORT, HOSTNAME, (e) => {
|
|
||||||
if (e == undefined) {
|
|
||||||
console.log("[o] Servidor (Alai) iniciado en el puerto %d", PORT)
|
|
||||||
} else {
|
|
||||||
console.error("Error express ", e)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
startWorker()
|
|
||||||
.then(e => {
|
|
||||||
console.log("[o] Worker de SIM de Alai iniciado")
|
|
||||||
})
|
|
||||||
.catch(e => {
|
|
||||||
console.log("[x] Error iniciando worker de SIM de Alai", e)
|
|
||||||
})
|
|
||||||
|
|
||||||
export default {}
|
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
import { AlaiAPI } from "#domain/AlaiAPI.js";
|
|
||||||
import axios, { AxiosError, AxiosResponse } from "axios";
|
|
||||||
import { Result } from "sim-shared/domain/Result.js";
|
|
||||||
import { env } from "#config/env/env.js";
|
|
||||||
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js";
|
|
||||||
import https from "https"
|
|
||||||
|
|
||||||
export class AlaiRepository {
|
|
||||||
constructor(
|
|
||||||
private httpClient: HttpClient
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private async manageRequest<E, T>(promiseReq: Promise<AxiosResponse<T>>): Promise<Result<string, T>> {
|
|
||||||
try {
|
|
||||||
const res = await promiseReq
|
|
||||||
return {
|
|
||||||
data: res.data
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (axios.isAxiosError(e)) {
|
|
||||||
const error = e as AxiosError
|
|
||||||
return {
|
|
||||||
error: error.code + " : " + String(error.response?.statusText)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
error: String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async login(httpsAgent: https.Agent): Promise<Result<string, AlaiAPI.LoginResponseDTO>> {
|
|
||||||
const alaiUrl = env.ALAI_API_URL
|
|
||||||
const endpoint = "/v1/auth/login"
|
|
||||||
const fullUrl = alaiUrl + endpoint
|
|
||||||
const data = {
|
|
||||||
"username": env.ALAI_USERNAME,
|
|
||||||
"password": env.ALAI_PASSWORD,
|
|
||||||
"brandID": env.ALAI_BRANDID
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const loginRes = await axios.post<AlaiAPI.LoginResponseDTO>(fullUrl, data, { httpsAgent })
|
|
||||||
return {
|
|
||||||
data: loginRes.data
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
if (axios.isAxiosError(e)) {
|
|
||||||
const error = e as AxiosError
|
|
||||||
return {
|
|
||||||
error: error.code + " : " + String(error.response?.statusText)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
error: String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Los orders son la unidad que envuelve las peticiones para garantizar ideponencia.
|
|
||||||
*/
|
|
||||||
public async createOrder() {
|
|
||||||
// POST
|
|
||||||
const endpoint = "/v1/order"
|
|
||||||
const data: AlaiAPI.CreateOrderDTO = {
|
|
||||||
type: "RETAIL",
|
|
||||||
salesChannel: "OWN_CALLCENTER",
|
|
||||||
status: "CONFIRMED",
|
|
||||||
packages: [{ id: env.ALAI_PACKAGE as string }],
|
|
||||||
subscriber: { id: env.ALAI_SUBSCRIBER_ID as string }
|
|
||||||
}
|
|
||||||
const promReq = this.httpClient.post<AlaiAPI.CreateOrderResponseDTO>(endpoint, data)
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async applyOrder(orderId: string) {
|
|
||||||
const endpoint = `/v1/order/${orderId}`
|
|
||||||
const params = new URLSearchParams([
|
|
||||||
["action", "APPLY"]
|
|
||||||
])
|
|
||||||
const promReq = this.httpClient.patch<AlaiAPI.ApplyOrderDTO>(endpoint, undefined, { params: params })
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ver estado del order para debug
|
|
||||||
*/
|
|
||||||
public async getOrder(orderId: string) {
|
|
||||||
const endpoint = `/v1/order/${orderId}`
|
|
||||||
const promReq = this.httpClient.post<AlaiAPI.CreateOrderResponseDTO>(endpoint, undefined)
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Antes se usaba PATCH /v1/sim/{iccid}/{orderId} pero en la docu ha pasado a POST
|
|
||||||
*/
|
|
||||||
public async createReserve(orderId: string, iccid: string): Promise<Result<string, AlaiAPI.CreateOrderResponseDTO>> {
|
|
||||||
const endpoint = `/v1/sim/${iccid}/order/${orderId}`
|
|
||||||
// Crear la reserva no usa datos en el body
|
|
||||||
const promReq = this.httpClient.post<AlaiAPI.CreateOrderResponseDTO>(endpoint, undefined)
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IMPORTANTE:
|
|
||||||
* - En el campo subscription viene el id para los cambios de estado, no se hacen
|
|
||||||
* sobre la sim, sino sobre la subscription
|
|
||||||
*/
|
|
||||||
public async getSimByICCID(iccid: string) {
|
|
||||||
const endpoint = `/v1/sim/${iccid}`
|
|
||||||
const promReq = this.httpClient.get<AlaiAPI.Sim | undefined>(endpoint, undefined)
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async pauseSubscription(subscriptionId: string) {
|
|
||||||
const endpoint = `/v1/subscription/${subscriptionId}`
|
|
||||||
// En teoria ahora se usa ["action", "BLOCK"] pero no he probado
|
|
||||||
const params = new URLSearchParams([
|
|
||||||
["action", "CHANGE_STATUS"]
|
|
||||||
])
|
|
||||||
const data = {
|
|
||||||
status: "BLOCKEDCORE"
|
|
||||||
}
|
|
||||||
const promReq = this.httpClient.patch<AlaiAPI.UpdateSubscriptionDTO | undefined>(endpoint, data, { params: params })
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async unPauseSubscription(subscriptionId: string) {
|
|
||||||
const endpoint = `/v1/subscription/${subscriptionId}`
|
|
||||||
// En teoria ahora se usa ["action", "UNBLOCK"] pero no he probado
|
|
||||||
const params = new URLSearchParams([
|
|
||||||
["action", "CHANGE_STATUS"]
|
|
||||||
])
|
|
||||||
const data = {
|
|
||||||
status: "ACTIVE"
|
|
||||||
}
|
|
||||||
const promReq = this.httpClient.patch<AlaiAPI.UpdateSubscriptionDTO | undefined>(endpoint, data, { params: params })
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async terminateSubscription(subscriptionId: string) {
|
|
||||||
const endpoint = `/v1/subscription/${subscriptionId}`
|
|
||||||
// Esta llamada si es de acuerdo a la docu
|
|
||||||
const params = new URLSearchParams([
|
|
||||||
["action", "TERMINATE"]
|
|
||||||
])
|
|
||||||
const promReq = this.httpClient.patch<AlaiAPI.UpdateSubscriptionDTO | undefined>(endpoint, undefined, { params: params })
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async changeExternalId(subscriptionId: string, externalId: string) {
|
|
||||||
const endpoint = `/v1/subscription/${subscriptionId}`
|
|
||||||
// Esta llamada si es de acuerdo a la docu
|
|
||||||
const params = new URLSearchParams([
|
|
||||||
["action", "MODIFY"]
|
|
||||||
])
|
|
||||||
const data = {
|
|
||||||
externalID: externalId
|
|
||||||
}
|
|
||||||
const promReq = this.httpClient.patch<AlaiAPI.UpdateSubscriptionDTO | undefined>(endpoint, data, { params: params })
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getSubscriptionById(subscriptionId: string) {
|
|
||||||
const endpoint = `/v1/subscription/${subscriptionId}`
|
|
||||||
const promReq = this.httpClient.get<AlaiAPI.Subscription | undefined>(endpoint)
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getImeiFromSubscription(subscriptionId: string) {
|
|
||||||
const endpoint = `/v1/subscription/${subscriptionId}/imei`
|
|
||||||
const promReq = this.httpClient.get<AlaiAPI.GetImeiSubscriptionDTO | undefined>(endpoint)
|
|
||||||
const res = await this.manageRequest(promReq)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import path from "path";
|
|
||||||
import fs from 'fs';
|
|
||||||
import { Result } from "sim-shared/domain/Result.js";
|
|
||||||
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
|
|
||||||
|
|
||||||
type TokenLine = {
|
|
||||||
id: number,
|
|
||||||
url: string,
|
|
||||||
user_name: string,
|
|
||||||
pass: string,
|
|
||||||
brand_id: string,
|
|
||||||
cert_file: string,
|
|
||||||
key_file: string,
|
|
||||||
ca_file: string,
|
|
||||||
p12_file: string,
|
|
||||||
cert_password: string,
|
|
||||||
token: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Repositorio para usar los tokens guardados en la bdd de intranet o en un archivo
|
|
||||||
*/
|
|
||||||
export class LegacyJWTTokenRepository {
|
|
||||||
constructor(
|
|
||||||
// En prod (no deberia usarse) tiene que apuntar a intranet
|
|
||||||
private pgClient: PgClient
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public async getTokenFromDB(): Promise<Result<string, string>> {
|
|
||||||
const query = "SELECT * FROM alai_api_credentials;"
|
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await this.pgClient.query<TokenLine>(query)
|
|
||||||
if (res.rowCount == 0) {
|
|
||||||
return {
|
|
||||||
error: "Error recuperando el token actual"
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
data: res.rows[0].token
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
error: String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static getTokenFromFile(dir: string, file: string): Result<string, string> {
|
|
||||||
try {
|
|
||||||
const tokenPath = path.join(dir, file)
|
|
||||||
const fileContent = fs.readFileSync(tokenPath).toString()
|
|
||||||
return {
|
|
||||||
data: fileContent
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return {
|
|
||||||
error: "[d!] No se ha podido leer el archivo del token de debug" + String(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sim-consumidor-alai",
|
|
||||||
"type": "module",
|
|
||||||
"description": "consumidor generico de eventos de alai",
|
|
||||||
"main": "index.ts",
|
|
||||||
"imports": {
|
|
||||||
"#config/*.js": {
|
|
||||||
"types": "./config/*.ts",
|
|
||||||
"default": "./config/*.js"
|
|
||||||
},
|
|
||||||
"#config/*": {
|
|
||||||
"types": "./config/*.ts",
|
|
||||||
"default": "./config/*.js"
|
|
||||||
},
|
|
||||||
"#infrastructure/*.js": {
|
|
||||||
"types": "./infrastructure/*.ts",
|
|
||||||
"default": "./infrastructure/*.js"
|
|
||||||
},
|
|
||||||
"#infrastructure/*": {
|
|
||||||
"types": "./infrastructure/*.ts",
|
|
||||||
"default": "./infrastructure/*.js"
|
|
||||||
},
|
|
||||||
"#domain/*.js": {
|
|
||||||
"types": "./domain/*.ts",
|
|
||||||
"default": "./domain/*.js"
|
|
||||||
},
|
|
||||||
"#domain/*": {
|
|
||||||
"types": "./domain/*.ts",
|
|
||||||
"default": "./domain/*.js"
|
|
||||||
},
|
|
||||||
"#aplication/*.js": {
|
|
||||||
"types": "./aplication/*.ts",
|
|
||||||
"default": "./aplication/*.js"
|
|
||||||
},
|
|
||||||
"#aplication/*": {
|
|
||||||
"types": "./aplication/*.ts",
|
|
||||||
"default": "./aplication/*.js"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
|
||||||
"build": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp -P .env package.json ../../dist/packages/sim-consumidor-alai/ && cp -r certificates/ ../../dist/packages/sim-consumidor-alai/",
|
|
||||||
"build:prod": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp -P package.json ../../dist/packages/sim-consumidor-alai/ && mkdir ../../dist/packages/sim-consumidor-alai/certificates",
|
|
||||||
"esbuild": "esbuild index.ts --platform=node",
|
|
||||||
"start": "node ../../dist/packages/sim-consumidor-alai/index.js",
|
|
||||||
"dev": "tsx watch index.ts"
|
|
||||||
},
|
|
||||||
"author": "",
|
|
||||||
"license": "ISC",
|
|
||||||
"packageManager": "yarn@4.12.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@tsconfig/node22": "*",
|
|
||||||
"amqplib": "^0.10.9",
|
|
||||||
"cors": "*",
|
|
||||||
"dotenv": "*",
|
|
||||||
"express": "*",
|
|
||||||
"sim-shared": "sim-shared:*",
|
|
||||||
"typescript": "*"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/amqplib": "^0.10.8",
|
|
||||||
"@types/cors": "*",
|
|
||||||
"@types/express": "*",
|
|
||||||
"@types/node": "*",
|
|
||||||
"@types/supertest": "*",
|
|
||||||
"prettier": "*",
|
|
||||||
"supertest": "*",
|
|
||||||
"tsc-alias": "^1.8.16",
|
|
||||||
"tsx": "*",
|
|
||||||
"vitest": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
# Alai
|
|
||||||
|
|
||||||
## Particularidades de las operaciones de Alai
|
|
||||||
|
|
||||||
TODO: Copiar de obsidian
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist",
|
|
||||||
"rootDir": "../../",
|
|
||||||
"paths": {
|
|
||||||
"#config/*": [
|
|
||||||
"./config/*"
|
|
||||||
],
|
|
||||||
"#infrastructure/*": [
|
|
||||||
"./infrastructure/*"
|
|
||||||
],
|
|
||||||
"#domain/*": [
|
|
||||||
"./domain/*"
|
|
||||||
],
|
|
||||||
"#aplication/*": [
|
|
||||||
"./aplication/*"
|
|
||||||
],
|
|
||||||
"config/*": [
|
|
||||||
"./config/*"
|
|
||||||
],
|
|
||||||
"infrastructure/*": [
|
|
||||||
"./infrastructure/*"
|
|
||||||
],
|
|
||||||
"domain/*": [
|
|
||||||
"./domain/*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"node_modules"
|
|
||||||
],
|
|
||||||
"include": [
|
|
||||||
"**/*.ts",
|
|
||||||
"**/*.d.ts",
|
|
||||||
"../../packages/sim-shared/**/*.ts"
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"index.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user