168 lines
5.9 KiB
JavaScript
168 lines
5.9 KiB
JavaScript
const axios = require('axios');
|
|
const createError = require('http-errors');
|
|
const config = require('../config');
|
|
const { getBusToken, createBusRequest } = require('../utils/busAuth');
|
|
const { findPatient } = require('../services/patient');
|
|
const { createDocumentReference, findDocumentReferenceById, findDocumentReferenceByUrl } = require('../services/documentReference');
|
|
const { logger } = require('../utils/logger');
|
|
const { v4: uuidv4 } = require('uuid');
|
|
|
|
|
|
const DOCUMENT_REFERENCE_RESOURCE_TYPE = "DocumentReference";
|
|
const IPS_DOCUMENT_RESOURCE_TYPE = 'Bundle';
|
|
const PATIENT_RESOURCE_TYPE = 'Patient';
|
|
const LIST_RESOURCE_TYPE = 'List';
|
|
|
|
|
|
const NATIONAL_ID_SYSTEM = 'https://federador.msal.gob.ar/patient-id';
|
|
const DNI_SYSTEM = 'http://www.renaper.gob.ar/dni';
|
|
const CUSTODIAN_ID_SYSTEM = 'http://federador.msal.gob.ar/uri';
|
|
const MASTER_ID_SYSTEM = 'urn:ietf:rfc:3986'
|
|
|
|
/**
|
|
* Procesa una transacción y devuelve los recursos persistidos en el servidor HAPI FHIR
|
|
* @param {*} transactionBundle
|
|
* @returns
|
|
*/
|
|
async function processTransaction(transactionBundle) {
|
|
const response = await axios.post(`${config.fhir.url}`, transactionBundle, {
|
|
headers: { 'Content-Type': 'application/fhir+json' },
|
|
});
|
|
if (response.status !== 200) {
|
|
throw createError(response.status, response.data.issue.diagnostics);
|
|
}
|
|
const processed = response.data;
|
|
const resources = [];
|
|
const responses = processed.entry.map(e => e.response);
|
|
for (i = 0; i < responses.length; i++) {
|
|
const resource = await axios.get(`${config.fhir.url}/${responses[i].location}`)
|
|
resources.push(resource);
|
|
}
|
|
return Promise.resolve(resources);
|
|
}
|
|
|
|
function extractResource(resources, resourceType) {
|
|
return resources.map(r => r.data).filter(r => r.resourceType === resourceType)[0];
|
|
}
|
|
|
|
function extractLocalIdentifier(patient) {
|
|
return patient.identifier.filter(i => {
|
|
return i.system !== NATIONAL_ID_SYSTEM && ((!!i.use && i.use === 'official') || (!i.use))
|
|
})[0];
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {*} documentReference
|
|
* @returns
|
|
*/
|
|
async function getBusCustodianIdentifier() {
|
|
return Promise.resolve({ system: CUSTODIAN_ID_SYSTEM, value: config.bus.issuer });
|
|
}
|
|
|
|
async function getBusPatientReference(patient, token) {
|
|
const localIdentifier = extractLocalIdentifier(patient);
|
|
const nationalPatientResponse = await findPatient(token, { identifier: `${localIdentifier.system}|${localIdentifier.value}` });
|
|
const nationalPatientBundle = nationalPatientResponse.data;
|
|
if (nationalPatientBundle.total == 0) {
|
|
throw createError(404, 'Patient does not exists');
|
|
}
|
|
// NOTE:La siguiente consulta no debería traer mas de un resultado.
|
|
return Promise.resolve(nationalPatientBundle.entry[0].fullUrl);
|
|
}
|
|
|
|
async function getLocalIPSDocumentReference(localIPSDocument) {
|
|
return Promise.resolve(`${config.baseURL}/fhir/Bundle/${localIPSDocument.id}`);
|
|
}
|
|
|
|
/**
|
|
* Crea un nuevo document refrence en el bus
|
|
* @param {*} token
|
|
* @param {*} localIPSDocument
|
|
* @param {*} localPatient
|
|
* @param {*} localDocumentReference
|
|
* @returns
|
|
*/
|
|
async function createBusDocumentReference(token, localIPSDocument, localPatient) {
|
|
const busCustodianIdentifier = await getBusCustodianIdentifier();
|
|
const busPatientReference = await getBusPatientReference(localPatient, token);
|
|
const localIPSDocumentReference = await getLocalIPSDocumentReference(localIPSDocument);
|
|
const busDocumentReference = {
|
|
resourceType: 'DocumentReference',
|
|
status: 'current',
|
|
masterIdentifier: {
|
|
use: 'usual',
|
|
system: MASTER_ID_SYSTEM,
|
|
value: `urn:uuid:${uuidv4()}`
|
|
},
|
|
type: {
|
|
coding: [
|
|
{
|
|
system: 'http://loinc.org',
|
|
code: '60591-5',
|
|
display: 'Patient Summary Document',
|
|
},
|
|
],
|
|
},
|
|
date: new Date().toISOString(),
|
|
subject: {
|
|
reference: busPatientReference
|
|
},
|
|
custodian: {
|
|
identifier: busCustodianIdentifier,
|
|
},
|
|
content: [
|
|
{
|
|
attachment: {
|
|
url: localIPSDocumentReference,
|
|
contentType: 'application/fhir+json'
|
|
},
|
|
},
|
|
],
|
|
};
|
|
const response = await createDocumentReference(token, busDocumentReference);
|
|
const created = await findDocumentReferenceByUrl(token, response.headers['location']);
|
|
return created.data;
|
|
}
|
|
|
|
/**
|
|
* ITI-65: Provide Document Bundle (MHD)
|
|
*
|
|
* POST /fhir/Bundle
|
|
*
|
|
* Flow:
|
|
* 1. Store the IPS Bundle in HAPI FHIR.
|
|
* 2. Resolve the patient national identifier via Bus $match.
|
|
* 3. Register a DocumentReference in the Bus pointing to the stored Bundle.
|
|
*
|
|
* Required header:
|
|
* x-custodian-id — Efector identifier value in the Federador MSAL system.
|
|
*/
|
|
async function provideDocumentBundle(req, res, next) {
|
|
try {
|
|
const transaction = req.body;
|
|
const token = await getBusToken(
|
|
config.bus.url,
|
|
config.bus.jwtSecret,
|
|
config.bus.issuer,
|
|
[config.bus.mpiScope, config.bus.documentRegistryScope].join(',')
|
|
);
|
|
|
|
// Paso 1: Ejecuto la transcción en el servidor hapi subyacente
|
|
const resources = await processTransaction(transaction);
|
|
// Paso 2: Obtengo el paciente local (tal como se guardo en la trasnaccion)
|
|
const localPatient = extractResource(resources, PATIENT_RESOURCE_TYPE);
|
|
// Paso 3: Obtengo el documento IPS local (tal como se guardo en la transacción)
|
|
const localIPSDocument = extractResource(resources, IPS_DOCUMENT_RESOURCE_TYPE)
|
|
// Paso 4: Creo el document reference en el indice de atenciones
|
|
const busDocumentReference = await createBusDocumentReference(token, localIPSDocument, localPatient);
|
|
|
|
return res.status(200).json(busDocumentReference);
|
|
|
|
} catch (err) {
|
|
next(err);
|
|
}
|
|
}
|
|
|
|
module.exports = { provideDocumentBundle };
|