520 lines
16 KiB
TypeScript
520 lines
16 KiB
TypeScript
import { ActionData, ActivationData } from "#domain/DTOs/objeniousapi.js"
|
|
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"
|
|
import { AxiosError } from "axios"
|
|
import { Result } from "sim-shared/domain/Result.js"
|
|
import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } from "sim-shared/domain/operationsRepository.port.js"
|
|
import assert from "node:assert"
|
|
import { OrderRepository } from "sim-shared/infrastructure/OrderRepository.js"
|
|
import { CreatePauseCancelTaskDTO, PauseCancelTaskRepository } from "#adapters/PauseCancelTaskRepository.js"
|
|
import { ObjeniousOperationsRepository } from "sim-shared/infrastructure/ObjeniousOperationRepository.js"
|
|
import { ObjeniousLinesRepository } from "sim-shared/infrastructure/ObjeniousLinesRepository.js"
|
|
import { error } from "node:console"
|
|
import { ObjeniousLine, ObjeniousLineDb } from "sim-shared/domain/objeniousLine.js"
|
|
|
|
// TODO:
|
|
// - Pasar a un archivo de DTOs
|
|
// - Mucha repeticion por funcion, deberia hacer una plantilla
|
|
|
|
export class SimUseCases {
|
|
private readonly httpClient: HttpClient
|
|
private readonly objeniousRepository: ObjeniousOperationsRepository
|
|
private readonly orderRepository: OrderRepository
|
|
private readonly pauseRepository: PauseCancelTaskRepository
|
|
private readonly objeniousLinesRepository: ObjeniousLinesRepository
|
|
constructor(args: {
|
|
httpClient: HttpClient,
|
|
operationRepository: ObjeniousOperationsRepository,
|
|
orderRepository: OrderRepository,
|
|
pauseRepository: PauseCancelTaskRepository,
|
|
objeniousLinesRepository: ObjeniousLinesRepository
|
|
}) {
|
|
this.httpClient = args.httpClient
|
|
this.objeniousRepository = args.operationRepository
|
|
this.orderRepository = args.orderRepository
|
|
this.pauseRepository = args.pauseRepository
|
|
this.objeniousLinesRepository = args.objeniousLinesRepository
|
|
}
|
|
|
|
private async logOperation(data: ObjeniousOperation) {
|
|
await this.objeniousRepository.createOperation({
|
|
...data
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Garantiza el flujo de todos los casos de uso de:
|
|
* - Petición según la acción
|
|
* - Control de errores
|
|
* - Siempre devuelve un Result
|
|
* - Almacena la operacion en la base de datos
|
|
* - Actualiza el estado del order
|
|
*
|
|
* Necesita:
|
|
* - Mas control según el codigo de error
|
|
*/
|
|
private generateUseCase<
|
|
PAYLOAD,
|
|
RESPONSETYPE extends { requestId: string }
|
|
>(args: {
|
|
correlation_id?: string,
|
|
url: string,
|
|
operation: string,
|
|
operationPayload: PAYLOAD,
|
|
iccid: string
|
|
onError?: (_: any) => void
|
|
// on code response??
|
|
}): () => Promise<Result<string, boolean>> {
|
|
return async () => {
|
|
const req = this.httpClient.client.post<RESPONSETYPE>(args.url, {
|
|
...args.operationPayload
|
|
})
|
|
|
|
try {
|
|
const response = await req;
|
|
|
|
if (response.status == 200) {
|
|
assert(response.data.requestId != undefined)
|
|
|
|
// Creacion de la operacion inicial, antes de tener los datos
|
|
const operation: ObjeniousOperation = {
|
|
operation: args.operation,
|
|
iccids: String(args.iccid),
|
|
status: "noMassID",
|
|
request_id: response.data.requestId,
|
|
correlation_id: args.correlation_id
|
|
}
|
|
|
|
// TODO: Esto tiene poco sentido si la operacion ya se
|
|
// tenia que haber creado en el generador
|
|
this.logOperation(operation)
|
|
.then().catch(e => console.error("Error login operation", e))
|
|
|
|
if (args.correlation_id != undefined) {
|
|
this.orderRepository.updateOrder({
|
|
correlation_id: args.correlation_id!,
|
|
new_status: "running", // Siempre es runing la primera vez que se consume
|
|
})
|
|
.then(e => console.log("Order actualizado: ", e))
|
|
.catch(e => console.error("Error actualizando order", args.correlation_id))
|
|
}
|
|
|
|
return <Result<string, boolean>>{
|
|
error: undefined,
|
|
data: true
|
|
}
|
|
} else {
|
|
return {
|
|
error: String(response.status),
|
|
data: undefined
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error(`[Sim.usecase] Error ${args.operation}`, (error as AxiosError).response?.status)
|
|
return {
|
|
error: "Error general de la peticion",
|
|
data: undefined
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public activate(activationData: ActivationData): () => Promise<Result<string, boolean>> {
|
|
const OPERATION_URL = "/actions/activateLine"
|
|
return async () => {
|
|
const iccid = activationData.identifier.identifiers
|
|
// Comporbación excepcional para saber si la linea está suspendida
|
|
const statusLinea = await this.objeniousRepository.getLinesAPI("ICCID", [String(iccid)])
|
|
if (statusLinea.data != undefined && statusLinea.data[0].status.networkStatus == "SUSPENDED") {
|
|
const res = await this.reActivate(activationData)()
|
|
return res;
|
|
}
|
|
|
|
const req = this.httpClient.client.post(OPERATION_URL, {
|
|
dueDate: activationData.dueDate,
|
|
identifier: activationData.identifier,
|
|
customerAccountCode: activationData.customerAccountCode,
|
|
offer: activationData.offer
|
|
})
|
|
|
|
try {
|
|
const response = await req
|
|
|
|
if (response.status == 200) {
|
|
console.log("Activacion con exito", response.data)
|
|
|
|
const operation: ObjeniousOperation = {
|
|
operation: "activate",
|
|
iccids: String(activationData.identifier.identifiers),
|
|
status: "noMassID",
|
|
request_id: response.data.requestId
|
|
}
|
|
|
|
this.logOperation(operation).then().catch(e => console.error(e))
|
|
|
|
return <Result<string, boolean>>{
|
|
error: undefined,
|
|
data: true
|
|
}
|
|
} else {
|
|
// muy mejorable el control de errores
|
|
return {
|
|
error: String(response.status),
|
|
data: undefined
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[Sim.usecase] Error activando ", (error as AxiosError).response?.status)
|
|
return {
|
|
error: "Error general de la peticion",
|
|
data: undefined
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public preActivate(preActivateData: ActionData): () => Promise<Result<string, boolean>> {
|
|
const OPERATION_URL = "/actions/preactivateLine"
|
|
return async () => {
|
|
const req = this.httpClient.client.post(OPERATION_URL, {
|
|
...preActivateData
|
|
})
|
|
|
|
try {
|
|
const resp = await req
|
|
if (resp.status == 200) {
|
|
console.log("Sim preactivada con exito", resp.data)
|
|
const operation: ObjeniousOperation = {
|
|
correlation_id: preActivateData.correlation_id,
|
|
operation: "preactivate",
|
|
iccids: String(preActivateData.identifier.identifiers),
|
|
status: "noMassID",
|
|
request_id: resp.data.requestId
|
|
}
|
|
|
|
this.logOperation(operation).then().catch(e => console.error(e))
|
|
return <Result<string, boolean>>{
|
|
error: undefined,
|
|
data: true
|
|
}
|
|
} else {
|
|
return <Result<string, boolean>>{
|
|
error: String(resp.status),
|
|
data: undefined
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("Error preactivacion", preActivateData)
|
|
return <Result<string, boolean>>{
|
|
error: "Error preactivando la sim" + preActivateData.identifier,
|
|
data: undefined
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public reActivate(reactivateData: ActionData): () => Promise<Result<string, boolean>> {
|
|
const OPERATION_URL = "/actions/reactivateLine"
|
|
return async () => {
|
|
const req = this.httpClient.client.post(OPERATION_URL, {
|
|
...reactivateData
|
|
})
|
|
|
|
try {
|
|
const response = await req
|
|
|
|
// Creacion de la operacion inicial, antes de tener los datos
|
|
const operation: ObjeniousOperation = {
|
|
operation: "reactivate",
|
|
iccids: reactivateData.identifier.identifiers[0],
|
|
status: "noMassID",
|
|
request_id: response.data.requestId,
|
|
correlation_id: reactivateData.correlation_id
|
|
}
|
|
|
|
// TODO: Esto tiene poco sentido si la operacion ya se
|
|
// tenia que haber creado en el generador
|
|
this.logOperation(operation)
|
|
.then().catch(e => console.error("Error login operation", e))
|
|
if (response.status == 200) {
|
|
console.log("[o] Sim solicitud de reactivacion ", response.data)
|
|
return <Result<string, boolean>>{
|
|
error: undefined,
|
|
data: true
|
|
}
|
|
} else {
|
|
return {
|
|
error: String(response.status),
|
|
data: undefined
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error("[x] Error reactivacion", (error as AxiosError).response?.status)
|
|
return <Result<string, boolean>>{
|
|
error: "Error reactivando la sim" + reactivateData.identifier,
|
|
data: undefined
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public suspend(suspendData: ActionData): () => Promise<Result<string, boolean>> {
|
|
const OPERATION_URL = "/actions/suspendLine"
|
|
return this.generateUseCase({
|
|
correlation_id: suspendData.correlation_id,
|
|
operationPayload: {
|
|
dueDate: suspendData.dueDate,
|
|
identifier: suspendData.identifier
|
|
},
|
|
url: OPERATION_URL,
|
|
iccid: suspendData.identifier.identifiers[0], //
|
|
operation: "suspend"
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Metodo muy especifico para obtener la fecha e activacion o en su defecto
|
|
* la actual para saber cuando se va a completar el periodo de test de una linea
|
|
*/
|
|
private async findActivationDate(actionData: ActionData) {
|
|
const iccid = actionData.identifier.identifiers
|
|
const lineData = await this.objeniousRepository.getLinesAPI("ICCID", iccid)
|
|
let activationDate = new Date()
|
|
// Si no se pueden sacar datos de la linea guardo momentaneamente el error
|
|
// pero no se cancela la operacion, el error puede ser de objenious y no nos
|
|
// puede afectar
|
|
//console.log("LineData", lineData.data)
|
|
if (lineData.error != undefined) {
|
|
console.error(lineData.error)
|
|
} else {
|
|
const activationDateStr = lineData.data[0].status.activationDate
|
|
if (activationDateStr != undefined && activationDateStr != "") {
|
|
activationDate = new Date(activationDateStr)
|
|
}
|
|
}
|
|
return activationDate
|
|
}
|
|
|
|
|
|
/**
|
|
* Paso previo a la suspension para evitar errores cuando el billing es test
|
|
*/
|
|
public stage_suspend(suspendData: ActionData): () => Promise<Result<string, boolean>> {
|
|
return async (): Promise<Result<string, boolean>> => {
|
|
const correlation_id = suspendData.correlation_id
|
|
const iccid = suspendData.identifier.identifiers
|
|
|
|
|
|
const operation: ObjeniousOperation = {
|
|
operation: "suspend",
|
|
iccids: iccid[0],
|
|
status: "running",
|
|
correlation_id: correlation_id
|
|
}
|
|
// No se registra hasta que no pase por la tabla de pausas
|
|
// this.logOperation(operation)
|
|
// .then().catch(e => console.error("Error login operation", e))
|
|
|
|
const fail = (error: string) => {
|
|
console.error("[Sim.usecases]", error)
|
|
if (correlation_id != undefined) {
|
|
this.orderRepository.updateOrder({
|
|
correlation_id: correlation_id,
|
|
new_status: "failed"
|
|
})
|
|
}
|
|
}
|
|
|
|
// TODO REGISTRAR EL ORDER
|
|
/*
|
|
if (correlation_id != undefined) {
|
|
await this.orderRepository.createOrder({
|
|
correlation_id: correlation_id,
|
|
order_type: "pause"
|
|
})
|
|
}
|
|
*/
|
|
let activationDate;
|
|
try {
|
|
activationDate = await this.findActivationDate(suspendData)
|
|
} catch (e) {
|
|
return {
|
|
error: String(e)
|
|
}
|
|
}
|
|
const newTask: CreatePauseCancelTaskDTO = {
|
|
iccid: iccid[0],
|
|
activation_date: activationDate,
|
|
next_check: undefined, // Que se haga instantaneamente al ser la primera
|
|
operation_type: "suspend",
|
|
action_data: suspendData
|
|
}
|
|
|
|
const taskCreated = await this.pauseRepository.addTask(newTask)
|
|
|
|
// Caso que la task no se pueda crear en la BDD
|
|
if (taskCreated.error != undefined) {
|
|
fail(taskCreated.error)
|
|
return {
|
|
error: taskCreated.error
|
|
}
|
|
}
|
|
|
|
// Caso que se haya creado en la BDD
|
|
if (correlation_id != undefined) {
|
|
this.orderRepository.updateOrder({
|
|
correlation_id: correlation_id,
|
|
new_status: "running"
|
|
})
|
|
}
|
|
|
|
return {
|
|
data: true
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Paso previo a la suspension para evitar errores cuando el billing es test
|
|
*/
|
|
public stage_terminate(terminateData: ActionData): () => Promise<Result<string, boolean>> {
|
|
return async (): Promise<Result<string, boolean>> => {
|
|
const correlation_id = terminateData.correlation_id
|
|
const iccid = terminateData.identifier.identifiers[0]
|
|
|
|
const activationDate = await this.findActivationDate(terminateData)
|
|
const newTask: CreatePauseCancelTaskDTO = {
|
|
iccid: iccid,
|
|
activation_date: activationDate,
|
|
next_check: undefined, // Que se haga instantaneamente al ser la primera
|
|
operation_type: "terminate",
|
|
action_data: terminateData
|
|
}
|
|
|
|
const taskCreated = await this.pauseRepository.addTask(newTask)
|
|
|
|
const operation: ObjeniousOperation = {
|
|
operation: "terminate",
|
|
iccids: iccid,
|
|
status: "running",
|
|
correlation_id: correlation_id
|
|
}
|
|
|
|
/**
|
|
this.logOperation(operation)
|
|
.then().catch(e => console.error("Error login operation", e))
|
|
*/
|
|
// Caso que la task no se pueda crear en la BDD
|
|
if (taskCreated.error != undefined) {
|
|
console.error("[Sim.usecases]", taskCreated.error)
|
|
if (correlation_id != undefined) {
|
|
this.orderRepository.updateOrder({
|
|
correlation_id: correlation_id,
|
|
new_status: "failed"
|
|
})
|
|
}
|
|
return {
|
|
error: taskCreated.error
|
|
}
|
|
}
|
|
|
|
// Caso que se haya creado en la BDD
|
|
if (correlation_id != undefined) {
|
|
this.orderRepository.updateOrder({
|
|
correlation_id: correlation_id,
|
|
new_status: "running"
|
|
})
|
|
}
|
|
|
|
return {
|
|
data: true
|
|
}
|
|
}
|
|
}
|
|
public terminate(terminationData: ActionData): () => Promise<Result<string, boolean>> {
|
|
const OPERATION_URL = "/actions/terminateLine"
|
|
return this.generateUseCase({
|
|
correlation_id: terminationData.correlation_id,
|
|
operationPayload: {
|
|
dueDate: terminationData.dueDate,
|
|
identifier: terminationData.identifier
|
|
},
|
|
url: OPERATION_URL,
|
|
iccid: terminationData.identifier.identifiers[0],
|
|
operation: "terminate"
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Calcula el tiempo que una linea ha estado en suspensión
|
|
*/
|
|
public async getSuspendedTime(iccid: string):
|
|
Promise<Result<string, { total_milliseconds: number, total_days: number }>> {
|
|
try {
|
|
const result = await this.objeniousRepository.getSuspendedTime(iccid);
|
|
if (result.error !== undefined) {
|
|
return { error: result.error as string, data: undefined };
|
|
}
|
|
return {
|
|
data: {
|
|
total_milliseconds: result.data!.total_milliseconds,
|
|
total_days: result.data!.total_days
|
|
}
|
|
};
|
|
} catch (error) {
|
|
console.error("[Sim.usecases] Error getting suspended time", error);
|
|
return { error: "Error getting suspended time", data: undefined };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Busqueda de líneas **en nuestro volcado** según una query y con paginacion
|
|
*/
|
|
public async getLinesByQuery(query: { status?: string | undefined }, pagination: { limit: number, offset: number })
|
|
: Promise<Result<string, {
|
|
data: ObjeniousLineDb[],
|
|
offset: number,
|
|
rowCount: number
|
|
}>> {
|
|
try {
|
|
|
|
const linesQuery = await this.objeniousLinesRepository.getLinesByStatus(query, pagination)
|
|
return {
|
|
data: linesQuery,
|
|
}
|
|
} catch (e) {
|
|
return {
|
|
error: String(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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]
|
|
}
|
|
|
|
}
|
|
}
|