Compare commits
72 Commits
WEBINT-338
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f7c052572 | |||
| 3d9e2a6e9b | |||
| f94ee799d0 | |||
| d1e5892a0d | |||
| b14464da39 | |||
| cf6204e231 | |||
| 9d8788db39 | |||
| 79ceb74604 | |||
| d02b1d48bf | |||
| 1fb50323b0 | |||
| 985c85da59 | |||
| 23c61097a8 | |||
| 5372045bd7 | |||
| 72d61b8376 | |||
| 526c094494 | |||
| 474f7b7c68 | |||
| f8692e3e2e | |||
| 62715fae34 | |||
| f8678a68bb | |||
| 8f8ae22f23 | |||
| 2611147eb3 | |||
| b0b3badd5c | |||
| 2b812098fb | |||
| 3146efec64 | |||
| 44e4b98e35 | |||
| 6adb4d5c95 | |||
| bcb1a28164 | |||
| d5798602e2 | |||
| 1f94a89520 | |||
| 44bbc8f17c | |||
| 9e6877c329 | |||
| d5883ba75e | |||
| 421d0aa705 | |||
| 69b5958296 | |||
| 86478b1073 | |||
| b9e3e1784f | |||
| 976cf1c3d2 | |||
| e4ba1576e5 | |||
| 4baa9f708f | |||
| 6fb25e6055 | |||
| 63698ee1aa | |||
| 410f659db0 | |||
| 08c972e720 | |||
| c4e4d87303 | |||
| 9c74fb9a7b | |||
| 1d7c2b2946 | |||
| d2c86396b1 | |||
| 3cf5c3695e | |||
| 7dda25fbfb | |||
| eefb7c5a79 | |||
| 13944a64d2 | |||
| 07e60690ab | |||
| 036ae20ac3 | |||
| 189de6c0fb | |||
| 113d9f3786 | |||
| 331d920379 | |||
| a615fc2b81 | |||
| f98097d11d | |||
| 3e76c3c931 | |||
| bb31efb271 | |||
| c9733113cf | |||
| 4c1d6ac2c4 | |||
| 07e7a0d457 | |||
| 48361ab33f | |||
| a3c7c224b1 | |||
| 324aec3001 | |||
| f78a333e1e | |||
| c91965567d | |||
| 858932f260 | |||
| a84f600fa2 | |||
| 4e02ea021d | |||
| 9ec127433d |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -16,8 +16,10 @@ node_modules
|
|||||||
#!.yarn/cache
|
#!.yarn/cache
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Certificados
|
||||||
*.pem
|
*.pem
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
|
||||||
dist/*
|
dist/*
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,12 @@ compressionLevel: mixed
|
|||||||
|
|
||||||
enableGlobalCache: false
|
enableGlobalCache: false
|
||||||
|
|
||||||
|
enableScripts: true
|
||||||
|
|
||||||
nodeLinker: node-modules
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
npmRegistryServer: "https://registry.npmjs.org/"
|
||||||
|
|
||||||
npmScopes:
|
npmScopes:
|
||||||
sf-alvar:
|
sf-alvar:
|
||||||
npmRegistryServer: "https://git.savefamilygps.net/api/packages/SaveFamily/npm/"
|
npmRegistryServer: "https://git.savefamilygps.net/api/packages/SaveFamily/npm/"
|
||||||
|
|
||||||
npmRegistryServer: "https://registry.npmjs.org/"
|
|
||||||
|
|||||||
@@ -43,3 +43,4 @@ La decisión del numero de reintentos y la cola de dlx se hace en los servicios,
|
|||||||
- **3000**: Gateway (sim-entrada-eventos)
|
- **3000**: Gateway (sim-entrada-eventos)
|
||||||
- **3001**: Consumidor NOS (sim-consumidor-nos)
|
- **3001**: Consumidor NOS (sim-consumidor-nos)
|
||||||
- **3002**: Consumidor Objenious (sim-consumidor-objenious)
|
- **3002**: Consumidor Objenious (sim-consumidor-objenious)
|
||||||
|
- **3003**: Consumidor Alai (sim-consumidor-alai)
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ 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
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ networks:
|
|||||||
external: true
|
external: true
|
||||||
internal:
|
internal:
|
||||||
driver: bridge
|
driver: bridge
|
||||||
|
volumes:
|
||||||
|
rabbitmq_data:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
rabbitmq-sim-broker:
|
rabbitmq-sim-broker:
|
||||||
@@ -28,6 +30,7 @@ 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
|
||||||
@@ -72,7 +75,10 @@ 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
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ pipeline {
|
|||||||
stage("🧱 Building") {
|
stage("🧱 Building") {
|
||||||
steps {
|
steps {
|
||||||
sh 'rm -rf dist/'
|
sh 'rm -rf dist/'
|
||||||
sh 'yarn run build'
|
sh 'yarn run build:prod'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stage("🏗 Deploying") {
|
stage("🏗 Deploying") {
|
||||||
@@ -38,14 +38,40 @@ 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"
|
||||||
@@ -54,12 +80,6 @@ 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",
|
||||||
|
|||||||
@@ -1,90 +1,92 @@
|
|||||||
{
|
{
|
||||||
"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": ["administrator"]
|
"tags": [
|
||||||
}
|
"administrator"
|
||||||
],
|
]
|
||||||
"vhosts": [
|
}
|
||||||
{
|
],
|
||||||
"name": "sim-vhost"
|
"vhosts": [
|
||||||
}
|
{
|
||||||
],
|
"name": "sim-vhost"
|
||||||
"permissions": [
|
}
|
||||||
{
|
],
|
||||||
"user": "RABBITMQ_USER_PLACEHOLDER",
|
"permissions": [
|
||||||
"vhost": "sim-vhost",
|
{
|
||||||
"configure": ".*",
|
"user": "RABBITMQ_USER_PLACEHOLDER",
|
||||||
"write": ".*",
|
"vhost": "sim-vhost",
|
||||||
"read": ".*"
|
"configure": ".*",
|
||||||
}
|
"write": ".*",
|
||||||
],
|
"read": ".*"
|
||||||
"topic_permissions": [],
|
}
|
||||||
"parameters": [],
|
],
|
||||||
"global_parameters": [
|
"topic_permissions": [],
|
||||||
{
|
"parameters": [],
|
||||||
"name": "cluster_name",
|
"global_parameters": [
|
||||||
"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": [
|
}
|
||||||
{
|
],
|
||||||
"vhost": "sim-vhost",
|
"policies": [
|
||||||
"name": "pol.sim.dlx",
|
{
|
||||||
"pattern": "sim.*",
|
"vhost": "sim-vhost",
|
||||||
"apply-to": "queues",
|
"name": "pol.sim.dlx",
|
||||||
"definition": {
|
"pattern": "sim.*",
|
||||||
"dead-letter-exchange": "sim.dlx"
|
"apply-to": "queues",
|
||||||
},
|
"definition": {
|
||||||
"priority": 7
|
"dead-letter-exchange": "sim.dlx"
|
||||||
}
|
},
|
||||||
],
|
"priority": 7
|
||||||
"exchanges": [
|
}
|
||||||
{
|
],
|
||||||
"name": "sim.exchange",
|
"exchanges": [
|
||||||
"vhost": "sim-vhost",
|
{
|
||||||
"type": "topic",
|
"name": "sim.exchange",
|
||||||
"durable": true,
|
"vhost": "sim-vhost",
|
||||||
"auto_delete": false,
|
"type": "topic",
|
||||||
"internal": false,
|
"durable": true,
|
||||||
"argurments": {}
|
"auto_delete": false,
|
||||||
},
|
"internal": false,
|
||||||
{
|
"argurments": {}
|
||||||
"name": "sim.dlx",
|
},
|
||||||
"vhost": "sim-vhost",
|
{
|
||||||
"type": "topic",
|
"name": "sim.dlx",
|
||||||
"durable": true,
|
"vhost": "sim-vhost",
|
||||||
"auto_delete": false,
|
"type": "topic",
|
||||||
"internal": false,
|
"durable": true,
|
||||||
"argurments": {}
|
"auto_delete": false,
|
||||||
}
|
"internal": false,
|
||||||
],
|
"argurments": {}
|
||||||
"queues": [
|
}
|
||||||
{
|
],
|
||||||
"name": "sim.logs",
|
"queues": [
|
||||||
"vhost": "sim-vhost",
|
{
|
||||||
"durable": true,
|
"name": "sim.logs",
|
||||||
"auto_delete": false,
|
"vhost": "sim-vhost",
|
||||||
"arguments": {}
|
"durable": true,
|
||||||
}
|
"auto_delete": false,
|
||||||
],
|
"arguments": {}
|
||||||
"bindings": [
|
}
|
||||||
{
|
],
|
||||||
"source": "sim.exchange",
|
"bindings": [
|
||||||
"vhost": "sim-vhost",
|
{
|
||||||
"destination": "sim.logs",
|
"source": "sim.exchange",
|
||||||
"destination_type": "queue",
|
"vhost": "sim-vhost",
|
||||||
"routing_key": "sim.#",
|
"destination": "sim.logs",
|
||||||
"arguments": {}
|
"destination_type": "queue",
|
||||||
}
|
"routing_key": "sim.#",
|
||||||
]
|
"arguments": {}
|
||||||
}
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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"
|
||||||
@@ -23,6 +24,7 @@ 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
|
||||||
|
|||||||
32
docs/sim-alai/Change External ID.yml
Normal file
32
docs/sim-alai/Change External ID.yml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
22
docs/sim-alai/IMEI of subscription.yml
Normal file
22
docs/sim-alai/IMEI of subscription.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
51
docs/sim-alai/Login.yml
Normal file
51
docs/sim-alai/Login.yml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
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
|
||||||
16
docs/sim-alai/New Order.yml
Normal file
16
docs/sim-alai/New Order.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
22
docs/sim-alai/SIM.yml
Normal file
22
docs/sim-alai/SIM.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
22
docs/sim-alai/Subscription.yml
Normal file
22
docs/sim-alai/Subscription.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
BIN
docs/sim-alai/certificates/alai_cert.p12
Normal file
BIN
docs/sim-alai/certificates/alai_cert.p12
Normal file
Binary file not shown.
7
docs/sim-alai/environments/local.yml
Normal file
7
docs/sim-alai/environments/local.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
name: local
|
||||||
|
color: "#2E8A54"
|
||||||
|
variables:
|
||||||
|
- name: baseurl
|
||||||
|
value: http://localhost:3002
|
||||||
|
- secret: true
|
||||||
|
name: token
|
||||||
15
docs/sim-alai/environments/prod.yml
Normal file
15
docs/sim-alai/environments/prod.yml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
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
|
||||||
26
docs/sim-alai/opencollection.yml
Normal file
26
docs/sim-alai/opencollection.yml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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,16 +6,80 @@ meta {
|
|||||||
|
|
||||||
post {
|
post {
|
||||||
url: {{baseurl}}/sim/activate
|
url: {{baseurl}}/sim/activate
|
||||||
body: formUrlEncoded
|
body: json
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body:json {
|
||||||
|
{
|
||||||
|
"iccid": "8934909001500561503"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8935103196306448300
|
iccid: 123
|
||||||
offer: SAVEFAMILY1
|
offer: mensual
|
||||||
}
|
}
|
||||||
|
|
||||||
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: 8
|
seq: 9
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Activation Email
|
name: Activation Email
|
||||||
type: http
|
type: http
|
||||||
seq: 6
|
seq: 8
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
20
docs/sim-api/Alai/Preactivate.bru
Normal file
20
docs/sim-api/Alai/Preactivate.bru
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
20
docs/sim-api/Alai/Select SIM.bru
Normal file
20
docs/sim-api/Alai/Select SIM.bru
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
8
docs/sim-api/Alai/folder.bru
Normal file
8
docs/sim-api/Alai/folder.bru
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
meta {
|
||||||
|
name: Alai
|
||||||
|
seq: 14
|
||||||
|
}
|
||||||
|
|
||||||
|
auth {
|
||||||
|
mode: inherit
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Cancel
|
name: Cancel
|
||||||
type: http
|
type: http
|
||||||
seq: 1
|
seq: 4
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
@@ -11,7 +11,7 @@ post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125068887054
|
iccid: 8933201125068889894
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Docs
|
name: Docs
|
||||||
type: http
|
type: http
|
||||||
seq: 12
|
seq: 11
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Health
|
name: Health
|
||||||
type: http
|
type: http
|
||||||
seq: 5
|
seq: 7
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
16
docs/sim-api/Nos/Select.bru
Normal file
16
docs/sim-api/Nos/Select.bru
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
meta {
|
||||||
|
name: Select
|
||||||
|
type: http
|
||||||
|
seq: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: {{baseurl}}/portugal/select
|
||||||
|
body: none
|
||||||
|
auth: inherit
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
8
docs/sim-api/Nos/folder.bru
Normal file
8
docs/sim-api/Nos/folder.bru
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
meta {
|
||||||
|
name: Nos
|
||||||
|
seq: 15
|
||||||
|
}
|
||||||
|
|
||||||
|
auth {
|
||||||
|
mode: inherit
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: France Suspended Lines
|
name: France Suspended Lines
|
||||||
type: http
|
type: http
|
||||||
seq: 17
|
seq: 16
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
8
docs/sim-api/Objenious/folder.bru
Normal file
8
docs/sim-api/Objenious/folder.bru
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
meta {
|
||||||
|
name: Objenious
|
||||||
|
seq: 16
|
||||||
|
}
|
||||||
|
|
||||||
|
auth {
|
||||||
|
mode: inherit
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Get pending orders
|
name: Get pending orders
|
||||||
type: http
|
type: http
|
||||||
seq: 11
|
seq: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
@@ -5,7 +5,7 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{baseurl}}/orders/
|
url: {{baseurl}}/orders/019dbeaf-8abb-7783-8b51-94fbd9f0b0df
|
||||||
body: none
|
body: none
|
||||||
auth: inherit
|
auth: inherit
|
||||||
}
|
}
|
||||||
@@ -5,15 +5,11 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: {{baseurl}}/orders/message_id/019c93d3-014a-711d-b958-03dd629be78d
|
url: {{baseurl}}/orders/message_id/019dbeaf-8abb-7783-8b51-94fbd9f0b0df
|
||||||
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
|
||||||
45
docs/sim-api/Orders/folder.bru
Normal file
45
docs/sim-api/Orders/folder.bru
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
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: 1
|
seq: 5
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: Preactivate
|
name: Preactivate
|
||||||
type: http
|
type: http
|
||||||
seq: 1
|
seq: 6
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
@@ -15,7 +15,9 @@ params:query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8933201125065160380
|
iccid: 8934909001500954922
|
||||||
|
offer: mensual
|
||||||
|
orderId: test
|
||||||
}
|
}
|
||||||
|
|
||||||
settings {
|
settings {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: ReActivate
|
name: ReActivate
|
||||||
type: http
|
type: http
|
||||||
seq: 13
|
seq: 12
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
@@ -11,7 +11,7 @@ post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
body:form-urlencoded {
|
body:form-urlencoded {
|
||||||
iccid: 8935103196306448300
|
iccid: 8934909001500561503
|
||||||
~offer: SAVEFAMILY1
|
~offer: SAVEFAMILY1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
20
docs/sim-api/Select.bru
Normal file
20
docs/sim-api/Select.bru
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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: 9
|
seq: 10
|
||||||
}
|
}
|
||||||
|
|
||||||
post {
|
post {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
docs {
|
docs {
|
||||||
Los endpoint tienen unos campos comunes de entrada:
|
Todos los endpoint tienen unos campos comunes de entrada:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
{
|
{
|
||||||
iccid: string,
|
iccid: string,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
vars {
|
vars {
|
||||||
baseurl: http://localhost:3000
|
baseurl: http://localhost:3000
|
||||||
|
baseAlai: http://localhost:3002
|
||||||
}
|
}
|
||||||
color: #2E8A54
|
color: #2E8A54
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
meta {
|
meta {
|
||||||
name: test proxy
|
name: test proxy
|
||||||
type: http
|
type: http
|
||||||
seq: 14
|
seq: 13
|
||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
|
|||||||
24
docs/sim-objenious/Alarms/Alarm by id.bru
Normal file
24
docs/sim-objenious/Alarms/Alarm by id.bru
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
28
docs/sim-objenious/Alarms/Alerts.bru
Normal file
28
docs/sim-objenious/Alarms/Alerts.bru
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
20
docs/sim-objenious/Alarms/All Alarms.bru
Normal file
20
docs/sim-objenious/Alarms/All Alarms.bru
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
8
docs/sim-objenious/Alarms/folder.bru
Normal file
8
docs/sim-objenious/Alarms/folder.bru
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
meta {
|
||||||
|
name: Alarms
|
||||||
|
seq: 21
|
||||||
|
}
|
||||||
|
|
||||||
|
auth {
|
||||||
|
mode: inherit
|
||||||
|
}
|
||||||
38
docs/sim-objenious/Alerts.bru
Normal file
38
docs/sim-objenious/Alerts.bru
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
meta {
|
||||||
|
name: Alerts
|
||||||
|
type: http
|
||||||
|
seq: 23
|
||||||
|
}
|
||||||
|
|
||||||
|
get {
|
||||||
|
url: https://api-getway.objenious.com/ws/alarms
|
||||||
|
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: ["8933201124059175967"]
|
||||||
|
}
|
||||||
|
|
||||||
|
vars:pre-request {
|
||||||
|
~id: 5187320
|
||||||
|
}
|
||||||
|
|
||||||
|
settings {
|
||||||
|
encodeUrl: true
|
||||||
|
timeout: 0
|
||||||
|
}
|
||||||
@@ -5,16 +5,16 @@ meta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get {
|
get {
|
||||||
url: https://api-getway.objenious.com/ws/lines?pageSize=1000&simStatus=ACTIVATED
|
url: https://api-getway.objenious.com/ws/lines?identifier.identifierType=ICCID&identifier.identifiers=8933201125065160455
|
||||||
body: formUrlEncoded
|
body: formUrlEncoded
|
||||||
auth: bearer
|
auth: bearer
|
||||||
}
|
}
|
||||||
|
|
||||||
params:query {
|
params:query {
|
||||||
pageSize: 1000
|
identifier.identifierType: ICCID
|
||||||
simStatus: ACTIVATED
|
identifier.identifiers: 8933201125065160455
|
||||||
~identifier.identifierType: ICCID
|
~pageSize: 1000
|
||||||
~identifier.identifiers: 8933201125065160455
|
~simStatus: ACTIVATED
|
||||||
}
|
}
|
||||||
|
|
||||||
auth:bearer {
|
auth:bearer {
|
||||||
|
|||||||
38
docs/sim-objenious/Consumption details.bru
Normal file
38
docs/sim-objenious/Consumption details.bru
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
41
docs/sim-objenious/Line by iccid.bru
Normal file
41
docs/sim-objenious/Line by iccid.bru
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "sim-eventos",
|
"name": "sim-eventos",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"packageManager": "yarn@4.12.0",
|
"packageManager": "yarn@4.14.1",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest watch",
|
"test": "vitest watch",
|
||||||
"build": "rm -rf ./dist && yarn workspaces foreach -Api run build && cp .env dist/ && yarn setup:runtime",
|
"build": "rm -rf ./dist && yarn workspaces foreach -Api run build && 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 setup:runtime && yarn workspaces foreach -Apiv run start",
|
"start": "yarn workspaces foreach -Apiv run start",
|
||||||
"typecheck": "npx tsc --noEmit",
|
"typecheck": "npx tsc --noEmit",
|
||||||
"dev": "yarn workspaces foreach -Apiv run dev",
|
"dev": "yarn workspaces foreach -Apiv run dev",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
|
|||||||
43
packages/sim-consumidor-alai/aplication/AlaiTokenManager.ts
Normal file
43
packages/sim-consumidor-alai/aplication/AlaiTokenManager.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
50
packages/sim-consumidor-alai/aplication/DebugTokenManager.ts
Normal file
50
packages/sim-consumidor-alai/aplication/DebugTokenManager.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
225
packages/sim-consumidor-alai/aplication/SimAlai.controller.ts
Normal file
225
packages/sim-consumidor-alai/aplication/SimAlai.controller.ts
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
type ErrorUsecase = {
|
||||||
|
msg: string,
|
||||||
|
stackTrace?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
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<ErrorUsecase, T>>): Promise<Result<ErrorUsecase, 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: {
|
||||||
|
msg: String(e),
|
||||||
|
stackTrace: 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 externalId = data.payload.orderId
|
||||||
|
|
||||||
|
const res = await this.tryUseCase(msg, this.uscases.activate({
|
||||||
|
iccid: iccid,
|
||||||
|
correlation_id: correlation_id,
|
||||||
|
}))
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public preactivate() {
|
||||||
|
return async (msg: ConsumeMessage) => {
|
||||||
|
console.log("[i] Evento preactivate ", msg)
|
||||||
|
const data = this.validateMsg(msg) as SimEvents.preActivation
|
||||||
|
const iccid = data.payload.iccid
|
||||||
|
const correlation_id = data.headers?.message_id
|
||||||
|
const externalId = data.payload.orderId
|
||||||
|
|
||||||
|
console.log("MSG:", data, data.headers)
|
||||||
|
|
||||||
|
const res = await this.tryUseCase(msg, this.uscases.preactivate({
|
||||||
|
iccid: iccid,
|
||||||
|
correlation_id: correlation_id,
|
||||||
|
externalId: externalId
|
||||||
|
}))
|
||||||
|
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, msg)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
**/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
79
packages/sim-consumidor-alai/aplication/SimAlai.router.ts
Normal file
79
packages/sim-consumidor-alai/aplication/SimAlai.router.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
/**
|
||||||
|
* 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<{ msg: string, stackTrace?: 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.preactivate()]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enruta el mensaje a la acción correspondiente basándose en la routing key
|
||||||
|
* TODO: No estoy seguro que deba meter el nack aqui
|
||||||
|
* - De moemento el ack-nack se gestiona en los controller, por si acaso hay casos
|
||||||
|
* limite en
|
||||||
|
*/
|
||||||
|
public route = async (msg: ConsumeMessage | null): Promise<void> => {
|
||||||
|
if (!msg) {
|
||||||
|
console.error("[Router] Mensaje vacío");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const action = this.extractAction(msg);
|
||||||
|
|
||||||
|
if (!action) {
|
||||||
|
console.error("[Router] La routing key no tiene una acción definida", msg.fields.routingKey);
|
||||||
|
this.eventBus.nack(msg)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const handler = this.routes.get(action);
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
console.error(`[Router] La acción '${action}' no tiene un controlador asociado`);
|
||||||
|
this.eventBus.nack(msg)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log("[Router] Ejecutando operación:", action);
|
||||||
|
|
||||||
|
// El controlador devuelve una función (thunk) que debe ser ejecutada
|
||||||
|
const executeParams = 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];
|
||||||
|
}
|
||||||
|
}
|
||||||
302
packages/sim-consumidor-alai/aplication/SimAlai.usecases.ts
Normal file
302
packages/sim-consumidor-alai/aplication/SimAlai.usecases.ts
Normal file
@@ -0,0 +1,302 @@
|
|||||||
|
/**
|
||||||
|
* 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 { 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";
|
||||||
|
|
||||||
|
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, stackTrace?: 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: stackTrace
|
||||||
|
}
|
||||||
|
|
||||||
|
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<{ msg: string, stackTrace?: string }, R>>,
|
||||||
|
args: T,
|
||||||
|
correlation_id?: string | undefined
|
||||||
|
) {
|
||||||
|
return async (): Promise<Result<{ msg: string, stackTrace?: 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.msg, res.error.stackTrace)
|
||||||
|
.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: {
|
||||||
|
msg: "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 sim = await this.alaiRepository.getSimByICCID(iccid)
|
||||||
|
if (sim.error != undefined) {
|
||||||
|
return sim
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sim.data == undefined) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: `La sim ${iccid} no se ha encontrado`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const subscriptionId = sim.data.subscription!.id
|
||||||
|
|
||||||
|
if (subscriptionId == undefined) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: `La sim ${iccid} no tiene un id de subscripción`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const activationRes = await this.alaiRepository.activateSubscription(subscriptionId)
|
||||||
|
return activationRes
|
||||||
|
|
||||||
|
}, 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 subscriptionid = subscription.data?.subscription?.id
|
||||||
|
const suspension = this.alaiRepository.pauseSubscription(subscriptionid!)
|
||||||
|
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
|
||||||
|
}
|
||||||
|
const subscriptionid = subscription.data?.subscription?.id
|
||||||
|
// TODO: Controlar que no se encuentre la subscription
|
||||||
|
const suspension = this.alaiRepository.unPauseSubscription(subscriptionid!)
|
||||||
|
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<{ msg: string, stackTrace?: string }, {
|
||||||
|
sim: AlaiAPI.Sim,
|
||||||
|
subscription?: AlaiAPI.Subscription,
|
||||||
|
imei?: AlaiAPI.GetImeiSubscriptionDTO
|
||||||
|
}>> {
|
||||||
|
const sim = await this.alaiRepository.getSimByICCID(iccid)
|
||||||
|
|
||||||
|
if (sim.error != undefined) {
|
||||||
|
return sim
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sim.data == undefined) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: `La sim ${iccid} no se ha encontrado`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// En este caso la tarjeta no se ha preactivado, por lo que no tiene subscripcion
|
||||||
|
if (sim.data.subscription == undefined) {
|
||||||
|
return {
|
||||||
|
data: {
|
||||||
|
sim: sim.data,
|
||||||
|
subscription: undefined,
|
||||||
|
imei: undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
55
packages/sim-consumidor-alai/aplication/SslService.ts
Normal file
55
packages/sim-consumidor-alai/aplication/SslService.ts
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
39
packages/sim-consumidor-alai/aplication/httpValidators.ts
Normal file
39
packages/sim-consumidor-alai/aplication/httpValidators.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { BodyValidator, Validator } from "sim-shared/aplication/BodyValidator.js";
|
||||||
|
|
||||||
|
const iccidNotNull = <Validator<{ iccid: unknown }>>{
|
||||||
|
field: "iccid",
|
||||||
|
errorMsg: "El iccid no está definido",
|
||||||
|
validationFunc: (a: { iccid: unknown }) => {
|
||||||
|
return (a.iccid != null && a.iccid != undefined)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const iccidValueOrArray = <Validator<{ iccid: unknown }>>{
|
||||||
|
field: "iccid",
|
||||||
|
errorMsg: "El iccid debe de ser un único valor o una lista",
|
||||||
|
validationFunc: (a: { iccid: unknown }) => {
|
||||||
|
return (typeof a.iccid == "string" || Array.isArray(a.iccid))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const iccidLongitudValidator = <Validator<{ iccid: string | string[] }>>{
|
||||||
|
field: "iccid",
|
||||||
|
errorMsg: "La longitud del iccid/s es incorrecta debera ser de 19 caracteres",
|
||||||
|
validationFunc: (a: { iccid: string | string[] }) => {
|
||||||
|
if (Array.isArray(a.iccid)) {
|
||||||
|
const res = (a.iccid as string[]).filter(e => e.length != 19)
|
||||||
|
if (res.length > 0) return false;
|
||||||
|
} else {
|
||||||
|
return (a.iccid as string).length == 19
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const iccidValidator = new BodyValidator<{ iccid: string | string[] }>(
|
||||||
|
[
|
||||||
|
iccidNotNull,
|
||||||
|
iccidValueOrArray,
|
||||||
|
iccidLongitudValidator,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
1
packages/sim-consumidor-alai/certificates/.debugToken
Normal file
1
packages/sim-consumidor-alai/certificates/.debugToken
Normal file
@@ -0,0 +1 @@
|
|||||||
|
eyJhbGciOiJIUzM4NCJ9.eyJiciI6InNhdmVmYW1pbHkiLCJpcCI6Ijg4LjE1LjE1Ny4xNjciLCJzdWIiOiJwYWxvbWFpYmFuZXoiLCJzIjoiRVdTMTY3MzRhYTM2MDY1M2EwIiwicG9zIjoic2F2ZWZhbWlseUNhYyIsImlkV3NVc2VyIjoiODYiLCJpc012bmEiOmZhbHNlLCJkb21haW4iOiJBbGFpfHNhdmVmYW1pbHkiLCJpYXQiOjE3Nzg2ODQ0NjIsImV4cCI6MTc3ODY5NTI2Mn0.wMWgjaOErm5clang7ErYzREU56okgpXWzq1zihT4lOfUDRQ005r-nCHJu7rpilj1
|
||||||
Binary file not shown.
61
packages/sim-consumidor-alai/config/env/env.ts
vendored
Normal file
61
packages/sim-consumidor-alai/config/env/env.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
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")
|
||||||
72
packages/sim-consumidor-alai/config/eventBus.config.ts
Normal file
72
packages/sim-consumidor-alai/config/eventBus.config.ts
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
20
packages/sim-consumidor-alai/config/httpClient.config.ts
Normal file
20
packages/sim-consumidor-alai/config/httpClient.config.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
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: {
|
||||||
|
"content-type": "application/json"
|
||||||
|
},
|
||||||
|
jwtManager: tokenManager,
|
||||||
|
//jwtManager: debugTokenManagr,
|
||||||
|
httpsAgent: httpsAgent
|
||||||
|
})
|
||||||
14
packages/sim-consumidor-alai/config/httpsAgent.ts
Normal file
14
packages/sim-consumidor-alai/config/httpsAgent.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
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
|
||||||
|
});
|
||||||
|
|
||||||
18
packages/sim-consumidor-alai/config/postgreConfig.ts
Normal file
18
packages/sim-consumidor-alai/config/postgreConfig.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Pool, QueryResult } from 'pg';
|
||||||
|
import { PgClient } from 'sim-shared/infrastructure/PgClient.js'
|
||||||
|
import { env } from './env/env.js';
|
||||||
|
|
||||||
|
// Configuracion de la conexion a la BDD, deberia ser la
|
||||||
|
// Misma para todos los servicios pero hasta que se unifique todo
|
||||||
|
// se hace una por servicio.
|
||||||
|
export const pgPool = new Pool({
|
||||||
|
user: env.POSTGRES_USER,
|
||||||
|
host: env.POSTGRES_HOST,
|
||||||
|
database: env.POSTGRES_DATABASE,
|
||||||
|
password: env.POSTGRES_PASSWORD,
|
||||||
|
port: Number(env.POSTGRES_PORT) || 5433,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const pgClient = new PgClient({
|
||||||
|
pool: pgPool
|
||||||
|
})
|
||||||
294
packages/sim-consumidor-alai/domain/AlaiAPI.ts
Normal file
294
packages/sim-consumidor-alai/domain/AlaiAPI.ts
Normal file
@@ -0,0 +1,294 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
62
packages/sim-consumidor-alai/domain/transformers.ts
Normal file
62
packages/sim-consumidor-alai/domain/transformers.ts
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
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<
|
||||||
|
{
|
||||||
|
sim: AlaiAPI.Sim,
|
||||||
|
subscription?: AlaiAPI.Subscription,
|
||||||
|
imeiSubscription?: AlaiAPI.GetImeiSubscriptionDTO
|
||||||
|
}
|
||||||
|
>> {
|
||||||
|
|
||||||
|
const billingStatus = (alaiSubscription == undefined) ? "AVAILABLE" : alaiStates.get(alaiSubscription?.status ?? "") ?? "UNKNOWN"
|
||||||
|
const networkStatus = (alaiSubscription == undefined) ? "AVAILABLE" : alaiNetworkStates.get(alaiSubscription.networkStatus) ?? "UNKNOWN"
|
||||||
|
|
||||||
|
const commonSim: CommonSim<{
|
||||||
|
sim: AlaiAPI.Sim,
|
||||||
|
subscription?: AlaiAPI.Subscription,
|
||||||
|
imeiSubscription?: AlaiAPI.GetImeiSubscriptionDTO
|
||||||
|
}> = {
|
||||||
|
company: "ALAI",
|
||||||
|
tariff: alaiSubscription?.name,
|
||||||
|
iccid: alaiSim.id,
|
||||||
|
msisdn: alaiSubscription?.lastMsisdnID,
|
||||||
|
billing_status: billingStatus,
|
||||||
|
network_status: networkStatus,
|
||||||
|
raw: {
|
||||||
|
subscription: alaiSubscription,
|
||||||
|
sim: alaiSim,
|
||||||
|
imeiSubscription: imeiSubscription
|
||||||
|
},
|
||||||
|
imei: imeiSubscription?.content[0]?.id ?? "0",
|
||||||
|
preactivation_date: (alaiSubscription != undefined) ? new Date(alaiSubscription.creationDate) : undefined,
|
||||||
|
activation_date: (alaiSubscription != undefined) ? new Date(alaiSubscription.firstActivationDate) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: commonSim
|
||||||
|
}
|
||||||
|
}
|
||||||
78
packages/sim-consumidor-alai/index.ts
Normal file
78
packages/sim-consumidor-alai/index.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
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 {}
|
||||||
228
packages/sim-consumidor-alai/infrastructure/AlaiRepository.ts
Normal file
228
packages/sim-consumidor-alai/infrastructure/AlaiRepository.ts
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
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"
|
||||||
|
|
||||||
|
type ErrorRepo = {
|
||||||
|
msg: string,
|
||||||
|
stackTrace?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AlaiRepository {
|
||||||
|
constructor(
|
||||||
|
private httpClient: HttpClient
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private async manageRequest<E, T>(promiseReq: Promise<AxiosResponse<T>>): Promise<Result<ErrorRepo, T>> {
|
||||||
|
try {
|
||||||
|
const res = await promiseReq
|
||||||
|
return {
|
||||||
|
data: res.data
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (axios.isAxiosError(e)) {
|
||||||
|
console.log("ERROR REQUEST ", e.response)
|
||||||
|
const error = e as AxiosError
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: error.code + " : " + String(error.response?.statusText),
|
||||||
|
stackTrace: JSON.stringify(error.response?.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: String(e),
|
||||||
|
stackTrace: String(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async login(httpsAgent: https.Agent): Promise<Result<ErrorRepo, 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: {
|
||||||
|
msg: error.code + " : " + String(error.response?.statusText),
|
||||||
|
stackTrace: String(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: 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<ErrorRepo, 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 activateSubscription(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 unPauseSubscription(subscriptionId: string) {
|
||||||
|
const endpoint = `/v1/subscription/${subscriptionId}`
|
||||||
|
// En teoria ahora se usa ["action", "UNBLOCK"] pero no he probado
|
||||||
|
const params = new URLSearchParams([
|
||||||
|
["action", "UNBLOCK"]
|
||||||
|
])
|
||||||
|
|
||||||
|
const rawParams = {
|
||||||
|
"action": "UNBLOCK"
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
status: "ACTIVE"
|
||||||
|
}
|
||||||
|
const promReq = this.httpClient.patch<AlaiAPI.UpdateSubscriptionDTO | undefined>(endpoint, undefined, { params: rawParams })
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
72
packages/sim-consumidor-alai/package.json
Normal file
72
packages/sim-consumidor-alai/package.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"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": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
5
packages/sim-consumidor-alai/readme.md
Normal file
5
packages/sim-consumidor-alai/readme.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Alai
|
||||||
|
|
||||||
|
## Particularidades de las operaciones de Alai
|
||||||
|
|
||||||
|
TODO: Copiar de obsidian
|
||||||
41
packages/sim-consumidor-alai/tsconfig.json
Normal file
41
packages/sim-consumidor-alai/tsconfig.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import { EventBus } from "sim-shared/domain/EventBus.port.js";
|
|||||||
import { Result } from "sim-shared/domain/Result.js";
|
import { Result } from "sim-shared/domain/Result.js";
|
||||||
import { SimEvents } from "sim-shared/domain/SimEvents.js";
|
import { SimEvents } from "sim-shared/domain/SimEvents.js";
|
||||||
import { iccidValidator } from "./httpValidators.js";
|
import { iccidValidator } from "./httpValidators.js";
|
||||||
|
import { error } from "node:console";
|
||||||
|
import { nosSimToCommonSim } from "#domain/transformers.js";
|
||||||
|
|
||||||
export class SimNosController {
|
export class SimNosController {
|
||||||
|
|
||||||
@@ -127,7 +129,7 @@ export class SimNosController {
|
|||||||
const validateBody = iccidValidator.validate(body);
|
const validateBody = iccidValidator.validate(body);
|
||||||
|
|
||||||
if (validateBody.error != undefined) {
|
if (validateBody.error != undefined) {
|
||||||
res.status(402).json(validateBody)
|
res.status(422).json(validateBody)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,12 +144,17 @@ export class SimNosController {
|
|||||||
res.status(500).json(usecaseRes)
|
res.status(500).json(usecaseRes)
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
res.send(usecaseRes.data)
|
const simComun = nosSimToCommonSim(usecaseRes.data)
|
||||||
|
res.status(200).send({ data: simComun })
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(200).json(validateBody)
|
res.status(501).json({
|
||||||
|
errors: {
|
||||||
|
msg: "No está implementada la busqueda por lista de iccid"
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +175,7 @@ export class SimNosController {
|
|||||||
res.status(500).json(usecaseRes)
|
res.status(500).json(usecaseRes)
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
res.status(200).send(usecaseRes.data)
|
res.status(200).send(usecaseRes)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ export const env = {
|
|||||||
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.APP_PORT),
|
APP_PORT: Number(process.env.NOS_PORT ?? 3001),
|
||||||
APP_HOST: String(process.env.APP_HOST),
|
APP_HOST: String(process.env.NOS_HOST ?? "0.0.0.0"),
|
||||||
|
|
||||||
// ESPECIFICO NOS
|
// ESPECIFICO NOS
|
||||||
NOS_BASE_URL: String(process.env.NOS_BASE_URL),
|
NOS_BASE_URL: String(process.env.NOS_BASE_URL),
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ export namespace NosApi {
|
|||||||
|
|
||||||
export type NetworkState = {
|
export type NetworkState = {
|
||||||
currentStateId: number
|
currentStateId: number
|
||||||
currentState: string
|
currentState: "active" | "barred" | "terminated"
|
||||||
isTransferring: boolean
|
isTransferring: boolean
|
||||||
lastTransferred: number
|
lastTransferred: number
|
||||||
isOnline: boolean
|
isOnline: boolean
|
||||||
@@ -112,7 +112,7 @@ export namespace NosApi {
|
|||||||
|
|
||||||
export type BillingState = {
|
export type BillingState = {
|
||||||
currentStateId: number
|
currentStateId: number
|
||||||
currentState: string
|
currentState: "active" | "terminated"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BarData = {
|
export type BarData = {
|
||||||
|
|||||||
39
packages/sim-consumidor-nos/domain/transformers.ts
Normal file
39
packages/sim-consumidor-nos/domain/transformers.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { CommonSim } from "sim-shared/domain/CommonSim.js";
|
||||||
|
import { NosApi } from "./NosAPI.js";
|
||||||
|
|
||||||
|
const billingStates = new Map<
|
||||||
|
NosApi.LineData["billingState"]["currentState"],
|
||||||
|
CommonSim<any>["billing_status"]>([
|
||||||
|
["active", "ACTIVE"],
|
||||||
|
["terminated", "TERMINATED"]
|
||||||
|
])
|
||||||
|
|
||||||
|
const networkStates = new Map<
|
||||||
|
NosApi.LineData["networkState"]["currentState"],
|
||||||
|
CommonSim<any>["network_status"]
|
||||||
|
>([
|
||||||
|
["active", "ACTIVE"],
|
||||||
|
["terminated", "TERMINATED"],
|
||||||
|
["barred", "SUSPENDED"]
|
||||||
|
])
|
||||||
|
|
||||||
|
export function nosSimToCommonSim(nosSim: NosApi.LineData): CommonSim<NosApi.LineData> {
|
||||||
|
const billingState = billingStates.get(nosSim.billingState.currentState) ?? "UNKNOWN"
|
||||||
|
const networkState = networkStates.get(nosSim.networkState.currentState) ?? "UNKNOWN"
|
||||||
|
|
||||||
|
const commonSim: CommonSim<NosApi.LineData> = {
|
||||||
|
company: "NOS",
|
||||||
|
tariff: nosSim.tariffName,
|
||||||
|
iccid: nosSim.physicalId,
|
||||||
|
msisdn: nosSim.subscriberId,
|
||||||
|
billing_status: billingState!,
|
||||||
|
network_status: networkState!,
|
||||||
|
raw: nosSim,
|
||||||
|
imei: nosSim.imei,
|
||||||
|
activation_date: new Date(nosSim.connectionDate),
|
||||||
|
termination_date: new Date(nosSim.terminateDate),
|
||||||
|
suspension_date: null // NOS no especifica la fecha de de 'barred' que equivale a la suspension
|
||||||
|
}
|
||||||
|
|
||||||
|
return commonSim
|
||||||
|
}
|
||||||
@@ -63,7 +63,7 @@ async function startWorker() {
|
|||||||
|
|
||||||
app.listen(PORT, HOSTNAME, (e) => {
|
app.listen(PORT, HOSTNAME, (e) => {
|
||||||
if (e == undefined) {
|
if (e == undefined) {
|
||||||
console.log("[o] Servidor iniciado en el puerto %d", PORT)
|
console.log("[o] Servidor (NOS) iniciado en el puerto %d", PORT)
|
||||||
} else {
|
} else {
|
||||||
console.error("Error express ", e)
|
console.error("Error express ", e)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,8 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp package.json ../../dist/packages/sim-consumidor-nos/",
|
"build": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp -P .env package.json ../../dist/packages/sim-consumidor-nos/",
|
||||||
|
"build:prod": "yarn tsc --project tsconfig.json && yarn tsc-alias && cp -P package.json ../../dist/packages/sim-consumidor-nos/",
|
||||||
"esbuild": "esbuild index.ts --platform=node",
|
"esbuild": "esbuild index.ts --platform=node",
|
||||||
"start": "node ../../dist/packages/sim-consumidor-nos/index.js",
|
"start": "node ../../dist/packages/sim-consumidor-nos/index.js",
|
||||||
"dev": "tsx watch index.ts"
|
"dev": "tsx watch index.ts"
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
# claves de Objenious
|
|
||||||
HOST=0.0.0.0
|
|
||||||
|
|
||||||
OBJ_PEM_PATH=./obj.pem
|
|
||||||
OBJ_AUTHORIZATION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ
|
|
||||||
OBJ_CLI_ASSERTION=XOc7FtwXD8hUX2SFVX94XSty8wkOmChkwDNF09O_aIxPubMDdFUdCDCB4zpzSIxi8nOcTg7r_LM_nmd5qm7uLbksf_XArjI8iAyhjKz_2BAXPhmvKs4Fc9f3vv5LDfCVrPB9lP8P7rJ66_qnWs4jvhLQxSfn29m96hgXeCf8oySdIDUjN2q9Js3KAS5LL52Ri6ryvUeO1PvMhaPQMWRqoHIqTV1wPfPtiqQwcjUPmu5GeW164Kq1JLgV3KaGzfCZ9Qv9lbv30EJrukXxWuLCAhBS0kzrBXZoWvf2pb9uh3Am_93_dDxiIGQfIap9ZU_m8ZD1HPgvZOMCY6ZkxQconQ
|
|
||||||
OBJ_CLIENT_ID=savefamily_rest_ws
|
|
||||||
OBJ_KID=xNfbMiyL1ORXGP8lElhcv8nVaG3EJKye4Lc1YoN3I1E
|
|
||||||
OBJ_BASE_URL=https://api-getway.objenious.com/ws
|
|
||||||
|
|
||||||
OBJ_CUSTOMER_CODE=9.49411.10
|
|
||||||
//OBJ_BASE_URL=https://api-getway.objenious.com/ws/test
|
|
||||||
@@ -7,6 +7,8 @@ import { ActionData } from "#domain/DTOs/objeniousapi.js";
|
|||||||
import { Request, Response } from "express"
|
import { Request, Response } from "express"
|
||||||
import { PaginationArgs, QueryPaginationArgs } from "sim-shared/domain/PaginationArgs.js";
|
import { PaginationArgs, QueryPaginationArgs } from "sim-shared/domain/PaginationArgs.js";
|
||||||
import { paginationValidator } from "./httpValidators.js";
|
import { paginationValidator } from "./httpValidators.js";
|
||||||
|
import { error } from "node:console";
|
||||||
|
import { objeniousSimToCommon } from "#domain/transformers.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* La clase usa generadores de funciones para mantener el contexto
|
* La clase usa generadores de funciones para mantener el contexto
|
||||||
@@ -236,7 +238,7 @@ export class SimController {
|
|||||||
|
|
||||||
const validationRes = paginationValidator.validate(paginationArgs)
|
const validationRes = paginationValidator.validate(paginationArgs)
|
||||||
if (validationRes.error != undefined) {
|
if (validationRes.error != undefined) {
|
||||||
res.status(402).json(validationRes)
|
res.status(422).json(validationRes)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,6 +254,28 @@ export class SimController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Una única linea para /select
|
||||||
|
*/
|
||||||
|
public queryLine() {
|
||||||
|
return async (req: Request, res: Response) => {
|
||||||
|
const queryParams = req.query
|
||||||
|
const queryArgs = {
|
||||||
|
iccid: queryParams.iccid as string // La validacion de iccid se ha tenido que hacer en el gateway
|
||||||
|
}
|
||||||
|
|
||||||
|
const line = await this.useCases.getLineByIccid(queryArgs.iccid)
|
||||||
|
if (line.error != undefined) {
|
||||||
|
res.status(line.error.code).json(line)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonLine = objeniousSimToCommon(line.data)
|
||||||
|
|
||||||
|
res.status(200).json({ data: commonLine })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
* - Loguear motivos de la no validacion
|
* - Loguear motivos de la no validacion
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class SimRouter {
|
|||||||
["pause", this.simController.suspend()],
|
["pause", this.simController.suspend()],
|
||||||
["cancel", this.simController.terminate()],
|
["cancel", this.simController.terminate()],
|
||||||
["reactivate", this.simController.reActivate()],
|
["reactivate", this.simController.reActivate()],
|
||||||
["preActivate", this.simController.preActivate()]
|
["preactivate", this.simController.preActivate()]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ export class SimUseCases {
|
|||||||
console.log("Sim preactivada con exito", resp.data)
|
console.log("Sim preactivada con exito", resp.data)
|
||||||
const operation: ObjeniousOperation = {
|
const operation: ObjeniousOperation = {
|
||||||
correlation_id: preActivateData.correlation_id,
|
correlation_id: preActivateData.correlation_id,
|
||||||
operation: "preActivate",
|
operation: "preactivate",
|
||||||
iccids: String(preActivateData.identifier.identifiers),
|
iccids: String(preActivateData.identifier.identifiers),
|
||||||
status: "noMassID",
|
status: "noMassID",
|
||||||
request_id: resp.data.requestId
|
request_id: resp.data.requestId
|
||||||
@@ -488,4 +488,32 @@ export class SimUseCases {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async getLineByIccid(iccid: string):
|
||||||
|
Promise<Result<{ msg: string, code: number }, ObjeniousLine>> {
|
||||||
|
const line = await this.objeniousRepository.getLineByIccid(iccid)
|
||||||
|
|
||||||
|
if (line.error != undefined) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: "Error general buscando la sim",
|
||||||
|
code: 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line.data.length == 0) {
|
||||||
|
return {
|
||||||
|
error: {
|
||||||
|
msg: "Sim no encontrada",
|
||||||
|
code: 204
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: line.data[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ async function buildQueues(channel: Channel) {
|
|||||||
await channel.assertExchange(EXCHANGES.DLX, "topic")
|
await channel.assertExchange(EXCHANGES.DLX, "topic")
|
||||||
await channel.assertExchange(EXCHANGES.MAIN, "topic")
|
await channel.assertExchange(EXCHANGES.MAIN, "topic")
|
||||||
|
|
||||||
await channel.assertQueue(QUEUES.OBJ)
|
await channel.assertQueue(QUEUES.OBJ, { durable: true })
|
||||||
await channel.assertQueue(QUEUES.OBJDLX)
|
await channel.assertQueue(QUEUES.OBJDLX, { durable: true })
|
||||||
await channel.assertQueue(QUEUES.OBJDEL, {
|
await channel.assertQueue(QUEUES.OBJDEL, {
|
||||||
durable: true,
|
durable: true,
|
||||||
arguments: {
|
arguments: {
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const DEFAULT_DATA_JWT = {
|
|||||||
iss: env.OBJ_CLIENT_ID,
|
iss: env.OBJ_CLIENT_ID,
|
||||||
aud: "https://idp.docapost.io/auth/realms/GETWAY",
|
aud: "https://idp.docapost.io/auth/realms/GETWAY",
|
||||||
jti: Date.now().toString(),
|
jti: Date.now().toString(),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function addIATHeaders(authHeaders: Object) {
|
function addIATHeaders(authHeaders: Object) {
|
||||||
|
|||||||
46
packages/sim-consumidor-objenious/domain/transformers.ts
Normal file
46
packages/sim-consumidor-objenious/domain/transformers.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { CommonSim } from "sim-shared/domain/CommonSim.js";
|
||||||
|
import { ObjeniousLine } from "sim-shared/domain/objeniousLine.js";
|
||||||
|
|
||||||
|
type ObjeniousBillingStates = ObjeniousLine["status"]["billingStatus"]
|
||||||
|
type ObjeniousNetworkStates = ObjeniousLine["status"]["networkStatus"]
|
||||||
|
|
||||||
|
//"PREACTIVATED" | "ACTIVE" | "SUSPENDED" | "TERMINATED" | "UNKNOWN",
|
||||||
|
const objeiousStates = new Map<ObjeniousBillingStates, CommonSim<any>["billing_status"]>([
|
||||||
|
["ACTIVATED", "ACTIVE"],
|
||||||
|
["CANCELED", "TERMINATED"],
|
||||||
|
["SUSPENDED", "SUSPENDED"],
|
||||||
|
["TEST", "PREACTIVATED"]
|
||||||
|
])
|
||||||
|
|
||||||
|
const objeiousNetworkStates = new Map<ObjeniousNetworkStates, CommonSim<any>["network_status"]>([
|
||||||
|
["ACTIVATED", "ACTIVE"],
|
||||||
|
["CANCELED", "TERMINATED"],
|
||||||
|
["SUSPENDED", "SUSPENDED"],
|
||||||
|
["BARRED", "SUSPENDED"]
|
||||||
|
])
|
||||||
|
|
||||||
|
export function objeniousSimToCommon(objSim: ObjeniousLine): CommonSim<ObjeniousLine> {
|
||||||
|
const status = objeiousStates.get(objSim.status.billingStatus) ?? "UNKNOWN"
|
||||||
|
const networkStatus = objeiousNetworkStates.get(objSim.status.networkStatus) ?? "UNKNOWN"
|
||||||
|
|
||||||
|
const preDate = objSim.status.preactivationDate
|
||||||
|
const actDate = objSim.status.activationDate
|
||||||
|
|
||||||
|
const preactivate = (preDate) ? new Date(preDate) : null
|
||||||
|
const activate = (actDate) ? new Date(actDate) : null
|
||||||
|
|
||||||
|
const common: CommonSim<ObjeniousLine> = {
|
||||||
|
company: "OBJ",
|
||||||
|
tariff: objSim.offer.code,
|
||||||
|
iccid: objSim.identifier.iccid,
|
||||||
|
msisdn: objSim.identifier.msisdn,
|
||||||
|
billing_status: status,
|
||||||
|
network_status: networkStatus,
|
||||||
|
raw: objSim,
|
||||||
|
imei: objSim.identifier.imei,
|
||||||
|
preactivation_date: preactivate,
|
||||||
|
activation_date: activate
|
||||||
|
}
|
||||||
|
|
||||||
|
return common
|
||||||
|
}
|
||||||
@@ -97,6 +97,7 @@ async function startWorker() {
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
app.get("/lines", simController.queryLines())
|
app.get("/lines", simController.queryLines())
|
||||||
|
app.get("/select", simController.queryLine())
|
||||||
|
|
||||||
|
|
||||||
assert.ok(port, "Puerto del servicio no definido")
|
assert.ok(port, "Puerto del servicio no definido")
|
||||||
|
|||||||
@@ -55,7 +55,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node --import tsx --test ./**/*.test.ts",
|
"test": "node --import tsx --test ./**/*.test.ts",
|
||||||
"dev": "tsx watch index.ts",
|
"dev": "tsx watch index.ts",
|
||||||
"build": "tsc --build && yarn tsc-alias -p tsconfig.json && cp .env package.json ../../dist/packages/sim-consumidor-objenious/",
|
"build": "tsc --build && yarn tsc-alias -p tsconfig.json && cp -P ./.env ./package.json ../../dist/packages/sim-consumidor-objenious/",
|
||||||
|
"build:prod": "tsc --build && yarn tsc-alias -p tsconfig.json && cp -P ./package.json ../../dist/packages/sim-consumidor-objenious/",
|
||||||
"start": "node ../../dist/packages/sim-consumidor-objenious/index.js",
|
"start": "node ../../dist/packages/sim-consumidor-objenious/index.js",
|
||||||
"type:test": "tsc --noEmit"
|
"type:test": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
|
|||||||
0
packages/sim-entrada-eventos/README.md
Normal file
0
packages/sim-entrada-eventos/README.md
Normal file
@@ -32,14 +32,23 @@ export class OrderController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getByQueueId() {
|
public getByQueueId() {
|
||||||
return this.controllerGenerator<{ correlation_id: string }, { correlation_id: string }>({
|
return this.controllerGenerator<{ uuid: string }, { correlation_id: string }>({
|
||||||
validator: uuidValidator,
|
validator: uuidValidator,
|
||||||
|
mapBody: (e) => ({ correlation_id: e.uuid }),
|
||||||
useCase: this.orderUseCases.getByQueueId(),
|
useCase: this.orderUseCases.getByQueueId(),
|
||||||
onError: (data, error) => { console.error(error) },
|
onError: (data, error) => { console.error(error) },
|
||||||
onSuccess: (data) => console.log(data)
|
onSuccess: (data) => console.log(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getByQuery() {
|
||||||
|
return this.controllerGenerator({
|
||||||
|
validator: undefined,
|
||||||
|
useCase: this.orderUseCases.getByQuery(),
|
||||||
|
onError: (data, error) => { console.error(error) },
|
||||||
|
onSuccess: (data) => console.log(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO:
|
* TODO:
|
||||||
@@ -77,7 +86,7 @@ export class OrderController {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Transformacion del body
|
// 2. Transformacion del body O => P
|
||||||
let data: P = body;
|
let data: P = body;
|
||||||
try {
|
try {
|
||||||
if (args.mapBody != undefined)
|
if (args.mapBody != undefined)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { OrderQuery } from "sim-shared/domain/Order.js";
|
||||||
import { PaginationArgs } from "sim-shared/domain/PaginationArgs.js";
|
import { PaginationArgs } from "sim-shared/domain/PaginationArgs.js";
|
||||||
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js";
|
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js";
|
||||||
|
|
||||||
@@ -36,4 +37,10 @@ export class OrderUsecases {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WIP
|
||||||
|
public getByQuery() {
|
||||||
|
return async (args: OrderQuery) => {
|
||||||
|
return await this.orderRepository.getOrdersByQuery(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { activationValidator, iccidValidator } from "./httpValidators.js"
|
|||||||
import { companyFromIccid } from "#domain/companies.js"
|
import { companyFromIccid } from "#domain/companies.js"
|
||||||
import { BodyValidator } from "sim-shared/aplication/BodyValidator.js"
|
import { BodyValidator } from "sim-shared/aplication/BodyValidator.js"
|
||||||
import { tryCatch } from "sim-shared/domain/Result.js"
|
import { tryCatch } from "sim-shared/domain/Result.js"
|
||||||
|
import { mapCompanyService } from "#config/servicesProxy.js"
|
||||||
|
import axios, { AxiosError, isAxiosError } from "axios"
|
||||||
|
|
||||||
|
|
||||||
export class SimController {
|
export class SimController {
|
||||||
@@ -208,6 +210,59 @@ export class SimController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select no pasa por la cola de eventos al ser de solo lectura.
|
||||||
|
* Cada uno de los servicios de los proveedores tiene que aderirse al
|
||||||
|
* modelo común de datos de SIM + campo "raw"
|
||||||
|
*
|
||||||
|
* De momento se va a buscar por iccid, mas adlante por movil u otro criterio
|
||||||
|
*/
|
||||||
|
public select() {
|
||||||
|
return async (req: Request, res: Response) => {
|
||||||
|
console.log("SELECT: ", req.query)
|
||||||
|
const iccid = req.query.iccid as string
|
||||||
|
const validationRes = iccidValidator.validate({ iccid: iccid })
|
||||||
|
if (validationRes.error != undefined) {
|
||||||
|
res.status(422).json({
|
||||||
|
errors: {
|
||||||
|
...validationRes.error
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const company = companyFromIccid(iccid)
|
||||||
|
const url = mapCompanyService.get(company)
|
||||||
|
const endpoint = "/select"
|
||||||
|
|
||||||
|
if (url == undefined) {
|
||||||
|
console.error("[x] Error buscando el servicio para el select del iccid ", iccid)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const respSelect = await axios.get(url + endpoint, { params: req.query })
|
||||||
|
res.json(respSelect.data)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
if (isAxiosError(err)) {
|
||||||
|
const axiosErr = err as AxiosError
|
||||||
|
res.status(axiosErr.status ?? 500).json(err)
|
||||||
|
} else {
|
||||||
|
res.status(500).json({
|
||||||
|
errors: {
|
||||||
|
msg: "Error general buscando la sim"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* -- WIP
|
||||||
|
* Esta funcion se plantea para guardar tarjetas que no han llegado desde
|
||||||
|
* un operador conocido
|
||||||
|
*/
|
||||||
public save() {
|
public save() {
|
||||||
return async (req: Request, res: Response) => {
|
return async (req: Request, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -164,15 +164,16 @@ export class SimUsecases {
|
|||||||
Promise<Result<string, { iccid: string, message_id: string, operation: "preactivation" }>> {
|
Promise<Result<string, { iccid: string, message_id: string, operation: "preactivation" }>> {
|
||||||
|
|
||||||
const preActivationEvent = <SimEvents.preActivation>{
|
const preActivationEvent = <SimEvents.preActivation>{
|
||||||
key: `sim.${args.compañia}.preActivate`,
|
key: `sim.${args.compañia}.preactivate`,
|
||||||
payload: {
|
payload: {
|
||||||
iccid: args.iccid
|
iccid: args.iccid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("[d] Pre - activation ", preActivationEvent)
|
|
||||||
await this.eventBus.publish([preActivationEvent])
|
const preActivationWithId = this.addMessage_id(preActivationEvent)
|
||||||
const preactivationWithId = this.addMessage_id(preActivationEvent)
|
console.log("[d] Pre - activation ", preActivationWithId)
|
||||||
const createdOrder = await this.saveOrder<SimEvents.preActivation>(preactivationWithId)
|
await this.eventBus.publish([preActivationWithId])
|
||||||
|
const createdOrder = await this.saveOrder<SimEvents.preActivation>(preActivationWithId)
|
||||||
if (createdOrder.error != undefined) {
|
if (createdOrder.error != undefined) {
|
||||||
console.error(createdOrder.error)
|
console.error(createdOrder.error)
|
||||||
return {
|
return {
|
||||||
@@ -270,5 +271,6 @@ export class SimUsecases {
|
|||||||
|
|
||||||
return this.eventBus.publish([cancelationEvent])
|
return this.eventBus.publish([cancelationEvent])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,10 @@ const offerExists = <Validator<{ offer: string }>>{
|
|||||||
validationFunc: (a: { offer: string }) => offers.has(a.offer),
|
validationFunc: (a: { offer: string }) => offers.has(a.offer),
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUuidv7 = <Validator<{ correlation_id?: string }>>{
|
const isUuidv7 = <Validator<{ uuid?: string }>>{
|
||||||
field: "correlation_id",
|
field: "uuid",
|
||||||
errorMsg: "El uuid no es un uuidv7 valido",
|
errorMsg: "El uuid no es un uuidv7 valido",
|
||||||
validationFunc: (a) => a.correlation_id != undefined && a.correlation_id.length < 36
|
validationFunc: (a) => a.uuid != undefined && a.uuid.length < 36
|
||||||
}
|
}
|
||||||
|
|
||||||
const definedId = <Validator<{ id?: number }>>{
|
const definedId = <Validator<{ id?: number }>>{
|
||||||
@@ -56,12 +56,27 @@ const validNumericId = <Validator<{ id?: number }>>{
|
|||||||
validationFunc: (e) => e.id! >= 0
|
validationFunc: (e) => e.id! >= 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Por un problema arrastrado de alai, se tiene que guardar el orderId del pedido
|
||||||
|
* de la sim en un campo de Alai.
|
||||||
|
*/
|
||||||
|
const ifAlaiOrderId = <Validator<{ iccid: string, orderId?: string }>>{
|
||||||
|
field: "orderId",
|
||||||
|
errorMsg: "Es necesario incluir un id de pedido (orderId) en las activaciones de Alai",
|
||||||
|
validationFunc: (e) => {
|
||||||
|
const company = companyFromIccid(e.iccid)
|
||||||
|
if (company == "alai" && e.orderId == undefined) return false
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const activationValidator = new BodyValidator<{ iccid: string, offer: string }>(
|
export const activationValidator = new BodyValidator<{ iccid: string, offer: string }>(
|
||||||
[
|
[
|
||||||
iccidRequired,
|
iccidRequired,
|
||||||
iccidLongitudValidator,
|
iccidLongitudValidator,
|
||||||
iccidWithValidCompany,
|
iccidWithValidCompany,
|
||||||
offerExists,
|
offerExists,
|
||||||
|
ifAlaiOrderId
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -73,7 +88,7 @@ export const iccidValidator = new BodyValidator<{ iccid: string }>(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
export const uuidValidator = new BodyValidator<{ correlation_id?: string }>([
|
export const uuidValidator = new BodyValidator<{ uuid?: string }>([
|
||||||
isUuidv7
|
isUuidv7
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ export const env = {
|
|||||||
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),
|
||||||
CONNECTIONS_URL: String(process.env.CONNECTIONS_URL),
|
CONNECTIONS_URL: String(process.env.CONNECTIONS_URL),
|
||||||
|
|
||||||
OBJENIOUS_CONSUMER_URL: process.env.OBJENIOUS_CONSUMER_URL,
|
OBJENIOUS_CONSUMER_URL: process.env.OBJENIOUS_CONSUMER_URL,
|
||||||
NOS_CONSUMER_URL: process.env.NOS_CONSUMER_URL,
|
NOS_CONSUMER_URL: process.env.NOS_CONSUMER_URL,
|
||||||
|
ALAI_CONSUMER_URL: process.env.ALAI_CONSUMER_URL,
|
||||||
};
|
};
|
||||||
|
|||||||
10
packages/sim-entrada-eventos/config/servicesProxy.ts
Normal file
10
packages/sim-entrada-eventos/config/servicesProxy.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { env } from "./env/index.js";
|
||||||
|
|
||||||
|
|
||||||
|
export const mapCompanyService = new Map([
|
||||||
|
["alai", env.ALAI_CONSUMER_URL],
|
||||||
|
["nos", env.NOS_CONSUMER_URL],
|
||||||
|
["objenious", env.OBJENIOUS_CONSUMER_URL]
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user