Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: '16.x'
node-version: '22.x'
- run: npm ci
- run: cp config.private.ts.example config.private.ts
- run: npm run lint
- run: npm run tsc
- run: NODE_OPTIONS=--max-old-space-size=4096 npm run tsc
- run: npm run test
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# TypeScript build
*.tsbuildinfo

# Archivos de configuración
config.private.ts

Expand All @@ -10,13 +13,17 @@ logs
**/*.map
*.js
**.js
!__mocks__/
!__mocks__/**/*.js
*.map
**.map

!swagger/static*/*.js
!jest.setup.js

# Dependency directories
node_modules
typings
typings/*
.vscode
.nvmrc
130 changes: 130 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Migraciones de Node.js — ANDES API

Este documento registra las migraciones del runtime de Node.js de ANDES API, incluyendo cambios de dependencias, compatibilidad, pruebas y tareas pendientes.

---

# Migración: Node.js 14 → 18

## Fecha
19-12-2025

## Resumen
Migración del runtime de Node.js 14 a Node.js 18.
Incluye ajustes en dependencias, tests y configuración de CI.

## Cambios principales

### Runtime
- Node.js >=18 <19
- Actualización de GitHub Actions a Node 18

### TypeScript
- Ajustes de tipado en `req.query` y `req.params`.
- Uso de cast explícito en métodos de schemas (`this as DocType`).
- `skipLibCheck: true` mantenido.

### Jest / Tests
- Se agregó `testTimeout` para soportar `mongodb-memory-server`.
- Eliminado uso de `jasmine.DEFAULT_TIMEOUT_INTERVAL`.

### Dependencias problemáticas
- `mongoose-gridfs` (posteriormente reemplazado en la migración a Node 22).
- `@lykmapipo/common`.
- Downgrade de `mime` y `flat` para compatibilidad CommonJS (overrides).
- Rebuild de módulos nativos (`libxmljs2`).

### MongoDB
- Warnings conocidos:
- `collection.update` (deprecated).
- `ensureIndex` (deprecated).
- No afectaron el funcionamiento observado.

## Estado final
- Build OK.
- Tests OK.
- API funcional en Node 18.

---

# Migración: Node.js 18 → 22

## Fecha
Octubre de 2026.

## Resumen
Migración del runtime de ANDES API a Node.js 22.11.0.

Incluye actualización de dependencias incompatibles, reemplazo de la implementación de GridFS, ajustes en los tests y validaciones funcionales de la API.

## Cambios principales

### Runtime y dependencias
- Actualización a Node.js 22.11.0.
- Actualización de `sharp` de 0.29.3 a 0.33.5 para compatibilidad con Node 22.
- Reemplazo de `node-sass` por Dart Sass (`sass`).
- Actualización de las dependencias y del archivo `package-lock.json`.

### GridFS
- Eliminación de la dependencia `mongoose-gridfs`.
- Implementación mediante `GridFSBucket` del driver nativo de MongoDB.
- Incorporación de utilidades compartidas para el acceso a GridFS.
- Adaptación de operaciones de almacenamiento, lectura y eliminación de archivos.
- Actualización de los controladores y rutas que utilizaban la implementación anterior.
- Conservación de la compatibilidad de lectura con archivos almacenados previamente.

### Jest / Tests
- Se mantiene la ejecución de tests con Jest y `mongodb-memory-server`.
- Incorporación de un mock de `@andes/log` para evitar operaciones de creación de índices durante los tests.
- El mock evita que los loggers intenten utilizar una conexión MongoDB no inicializada, provocando errores de buffering timeout.
- Resultado: 6 suites y 43 tests satisfactorios.
- Jest finaliza correctamente sin operaciones pendientes relacionadas con `@andes/log`.

### MongoDB y conexiones
- Se deshabilitó la creación automática de índices de Mongoose mediante `autoIndex: false`.
- Se mantiene temporalmente el mecanismo de conexión anterior del driver MongoDB.
- Se conserva la configuración existente de reconexión.
- No se modificó el comportamiento de creación explícita de índices de `@andes/log`.

### Validaciones funcionales
- Inicio correcto de la API con Node 22.
- Lectura y escritura de datos en MongoDB.
- Visualización y descarga de archivos históricos.
- Subida y descarga de archivos nuevos mediante GridFS.
- Generación y descarga de documentos PDF.
- Registro de logs.
- Compilación TypeScript y tests satisfactorios.
- Ejecución de la versión migrada en servidores de prueba.

## Advertencias conocidas

### Node.js
- `DEP0040`: uso del módulo obsoleto `punycode` desde `tough-cookie`.

### MongoDB
- Advertencia del mecanismo antiguo de Server Discovery and Monitoring.
- Métodos heredados como `collection.update` y `ensureIndex`.

### Sass
- Uso de `@import` en archivos SCSS de generación de PDF.
- Dart Sass recomienda migrar a `@use` y `@forward`.

Estas advertencias no impidieron el funcionamiento observado durante las pruebas.

## Mejoras pendientes

1. **@andes/log:** evitar por defecto la creación automática de índices durante la inicialización de `Logger`, permitiendo su creación explícita. Revisar posteriormente la necesidad del mock de Jest.
2. **MongoDB / Unified Topology:** migrar a `useUnifiedTopology: true`, eliminar `reconnectTries` y `reconnectInterval` y validar el comportamiento ante desconexiones y reconexiones.
3. **connections.ts:** revisar la necesidad de mantener conexiones independientes a la base de logs.
4. **Mongoose y driver MongoDB:** evaluar una actualización a versiones con soporte vigente y modernizar la configuración de conexiones.
5. **punycode:** identificar y actualizar la dependencia responsable de la advertencia.
6. **Sass:** migrar las directivas `@import` a `@use` y `@forward`.
7. **GridFS:** completar pruebas de archivos inexistentes, eliminación y lectura posterior a la eliminación.

## Estado final
- Build TypeScript OK.
- 6 suites y 43 tests OK.
- API funcional en Node 22.11.0.
- Operaciones principales de GridFS validadas.
- Pruebas funcionales satisfactorias en entorno de pruebas.
- Mejoras no bloqueantes documentadas para próximas actualizaciones.
31 changes: 31 additions & 0 deletions __mocks__/@andes/log.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions __mocks__/@andes/log.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Mock de @andes/log utilizado en los tests unitarios.
*
* La implementación real de Logger intenta crear índices en MongoDB
* durante su instanciación, utilizando la conexión Connections.logs.
*
* En los tests unitarios esa conexión no está inicializada, por lo que
* las operaciones createIndex quedan pendientes y eventualmente
* producen errores de buffering timeout, impidiendo que Jest finalice.
*
* Este mock evita esos efectos secundarios y permite ejecutar los tests
* sin depender de la conexión a la base de datos de logs.
*
* TODO: Revisar este mock cuando @andes/log permita desactivar
* la creación automática de índices.
*/

export class Logger {
log = jest.fn();
info = jest.fn();
warn = jest.fn();
error = jest.fn();
debug = jest.fn();

constructor(options?: any) {
// No se inicializan conexiones ni índices.
}
}
2 changes: 1 addition & 1 deletion auth/auth.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ export class Auth {
try {
let tokenSettings;
if (req.query.hudsToken) {
tokenSettings = Auth.decode(req.query.hudsToken);
tokenSettings = Auth.decode(String(req.query.hudsToken));
return (String(tokenSettings.paciente) === String(paciente));
}
} catch (e) {
Expand Down
10 changes: 5 additions & 5 deletions auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { APP_DOMAIN, userScheduler } from './../config.private';
import { Profesional } from './../core/tm/schemas/profesional';
import { MailOptions, renderHTML, sendMail } from './../utils/roboSender/sendEmail';
import { Auth } from './auth.class';
import { AuthUsers } from './schemas/authUsers';
import { AuthUsers, IAuthUsers } from './schemas/authUsers';
import * as crypto from 'crypto';
const sha1Hash = require('sha1');

Expand Down Expand Up @@ -393,14 +393,14 @@ export async function createUser(data) {
const organizacion = await Organizacion.findOne({ matriculacion: true });
const permisos = getPermisosByType(data.tipoPermisos);
const organizaciones = [{
_id: organizacion._id,
nombre: organizacion.nombre,
_id: organizacion?._id,
nombre: organizacion?.nombre,
activo: true,
permisos,
perfiles: [],
lastLogin: new Date()
}];
user.organizaciones = organizaciones;
(user.organizaciones as any) = organizaciones;

user.audit(userScheduler);
return await user.save();
Expand All @@ -409,7 +409,7 @@ export async function createUser(data) {
export async function getTemporyTokenGenerarUsuario(username) {
const organizacion = await Organizacion.findOne({ matriculacion: true });
const permisos = getPermisosByType('generarUsuario');
return Auth.generateUserTokenTemporaly(username, permisos, organizacion._id);
return Auth.generateUserTokenTemporaly(username, permisos, organizacion?._id);
}

function getPermisosByType(tipoPermisos) {
Expand Down
4 changes: 4 additions & 0 deletions auth/schemas/authUsers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ export interface IAuthUsers {
}[];
lastLogin: Date;
fechaVencimiento?: Date;
createdAt: Date;
createdBy: any;
updatedAt?: Date;
updatedBy?: any;
}[];
lastLogin: Date;
tipo?: String;
Expand Down
8 changes: 6 additions & 2 deletions auth/schemas/permisos-organizaciones.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Schema, model, Types } from 'mongoose';
import { Schema, Types, SchemaTypes } from 'mongoose';
import { AuditPlugin } from '@andes/mongoose-plugin-audit';


Expand All @@ -12,7 +12,11 @@ export const PermisosOrganizacionesSchema = new Schema({
nombre: String
}],
lastLogin: Date,
fechaVencimiento: Date
fechaVencimiento: Date,
createdAt: { type: Date, required: false },
createdBy: { type: SchemaTypes.Mixed, required: false },
updatedAt: { type: Date, required: false },
updatedBy: { type: SchemaTypes.Mixed, required: false }
});
PermisosOrganizacionesSchema.plugin(AuditPlugin);

4 changes: 2 additions & 2 deletions connect/fhir/routes/organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ router.get('/organization/([\$])match', async (req, res, next) => {
RegExp('^.*' + req.query.name + '.*$', 'i');
}

if (req.query.identifier) {
opciones['codigo.sisa'] = utils.makePattern(req.query.identifier);
if (req.query.identifier as any) {
opciones['codigo.sisa'] = utils.makePattern(req.query.identifier as any);
}

const organizacionesFHIR = [];
Expand Down
6 changes: 3 additions & 3 deletions connect/fhir/routes/practitioner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ router.get('/practitioner/([\$])match', async (req, res, next) => {

if (req.query.family) {
opciones['apellido'] =
RegExp('^.*' + req.query.family + '.*$', 'i');
RegExp('^.*' + req.query.family as any + '.*$', 'i');
}

if (req.query.identifier) {
opciones['documento'] = utils.makePattern(req.query.identifier);
if (req.query.identifier as any) {
opciones['documento'] = utils.makePattern(req.query.identifier as any);
}

const profesionalesFHIR = [];
Expand Down
40 changes: 36 additions & 4 deletions connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class Connections {
(mongoose as any).Promise = global.Promise;
mongoose.plugin(schemaDefaults);


// Configura logger de consultas
const queryLogger = debug('mongoose');
if (queryLogger.enabled) {
Expand All @@ -44,18 +45,49 @@ export class Connections {
}


// Opciones comunes para todas las conexiones MongoDB
const mongoOptions = {
useNewUrlParser: true,
autoIndex: false
};

// Conecta y configura conexiones

// 1. PRINCIPAL
mongoose.connect(configPrivate.hosts.mongoDB_main.host, configPrivate.hosts.mongoDB_main.options);
mongoose.connect(
configPrivate.hosts.mongoDB_main.host,
{
...configPrivate.hosts.mongoDB_main.options,
...mongoOptions
}
);
this.main = mongoose.connection;

// 2. PUCO
this.puco = mongoose.createConnection(configPrivate.hosts.mongoDB_puco.host, configPrivate.hosts.mongoDB_puco.options);
this.puco = mongoose.createConnection(
configPrivate.hosts.mongoDB_puco.host,
{
...configPrivate.hosts.mongoDB_puco.options,
...mongoOptions
}
);

// 3. LOGGER
loggerConnections.initialize(configPrivate.logDatabase.log.host, configPrivate.logDatabase.log.options);
loggerConnections.initialize(
configPrivate.logDatabase.log.host,
{
...configPrivate.logDatabase.log.options,
...mongoOptions
}
);

this.logs = mongoose.createConnection(configPrivate.logDatabase.log.host, configPrivate.logDatabase.log.options);
this.logs = mongoose.createConnection(
configPrivate.logDatabase.log.host,
{
...configPrivate.logDatabase.log.options,
...mongoOptions
}
);

// Configura eventos
this.configEvents('main', this.main);
Expand Down
Loading
Loading