Compare commits

...

3 Commits

8 changed files with 110 additions and 118 deletions

View File

@ -11,9 +11,8 @@ const { getBusToken } = require('../utils/busAuth');
async function createPatient(req, res, next) { async function createPatient(req, res, next) {
try { try {
const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope); const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope);
const response = await busCreatePatient(token, req.body) const patient = await busCreatePatient(token, req.body)
const patientResponse = await findPatientByUrl(token, response.headers['location']); res.status(200).json(patient);
res.status(200).json(patientResponse.data);
} catch (err) { } catch (err) {
next(err); next(err);
} }
@ -26,8 +25,8 @@ async function createPatient(req, res, next) {
async function updatePatient(req, res, next) { async function updatePatient(req, res, next) {
try { try {
const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope); const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope);
const response = await busUpdatePatient(token, req.body) const patient = await busUpdatePatient(token, req.body)
res.status(200).json(response.data); res.status(200).json(patient);
} catch (err) { } catch (err) {
next(err); next(err);
} }

View File

@ -3,9 +3,10 @@ const createError = require('http-errors');
const config = require('../config'); const config = require('../config');
const { getBusToken, createBusRequest } = require('../utils/busAuth'); const { getBusToken, createBusRequest } = require('../utils/busAuth');
const { findPatient } = require('../services/patient'); const { findPatient } = require('../services/patient');
const { createDocumentReference, findDocumentReferenceById, findDocumentReferenceByUrl } = require('../services/documentReference'); const { createDocumentReference, findDocumentReferenceById } = require('../services/documentReference');
const { logger } = require('../utils/logger'); const { logger } = require('../utils/logger');
const { v4: uuidv4 } = require('uuid'); const { v4: uuidv4 } = require('uuid');
const { getResourceByUrl, processDocumentBundleTransaction } = require('../services/fhir');
const DOCUMENT_REFERENCE_RESOURCE_TYPE = "DocumentReference"; const DOCUMENT_REFERENCE_RESOURCE_TYPE = "DocumentReference";
@ -19,30 +20,9 @@ const DNI_SYSTEM = 'http://www.renaper.gob.ar/dni';
const CUSTODIAN_ID_SYSTEM = 'http://federador.msal.gob.ar/uri'; const CUSTODIAN_ID_SYSTEM = 'http://federador.msal.gob.ar/uri';
const MASTER_ID_SYSTEM = 'urn:ietf:rfc:3986' 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) { function extractResource(resources, resourceType) {
return resources.map(r => r.data).filter(r => r.resourceType === resourceType)[0]; return resources.filter(r => r.resourceType === resourceType)[0];
} }
function extractLocalIdentifier(patient) { function extractLocalIdentifier(patient) {
@ -51,43 +31,8 @@ function extractLocalIdentifier(patient) {
})[0]; })[0];
} }
/** function generateDocumentReferenceResource(subjectReference, bundleUrl) {
* const documentRefernece = {
* @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', resourceType: 'DocumentReference',
status: 'current', status: 'current',
masterIdentifier: { masterIdentifier: {
@ -106,23 +51,29 @@ async function createBusDocumentReference(token, localIPSDocument, localPatient)
}, },
date: new Date().toISOString(), date: new Date().toISOString(),
subject: { subject: {
reference: busPatientReference reference: subjectReference
}, },
custodian: { custodian: {
identifier: busCustodianIdentifier, identifier: { system: CUSTODIAN_ID_SYSTEM, value: config.bus.issuer },
}, },
content: [ content: [
{ {
attachment: { attachment: {
url: localIPSDocumentReference, url: bundleUrl,
contentType: 'application/fhir+json' contentType: 'application/fhir+json'
}, },
}, },
], ],
}; };
const response = await createDocumentReference(token, busDocumentReference); return documentRefernece;
const created = await findDocumentReferenceByUrl(token, response.headers['location']); }
return created.data;
async function getResourcesFromTransactionResponse(transactionResponse) {
const promises = transactionResponse.entry.map(async (e) => {
const resource = getResourceByUrl(`${config.fhir.url}/${e.response.location}`);
return resource;
});
return Promise.all(promises);
} }
/** /**
@ -149,15 +100,30 @@ async function provideDocumentBundle(req, res, next) {
); );
// Paso 1: Ejecuto la transcción en el servidor hapi subyacente // Paso 1: Ejecuto la transcción en el servidor hapi subyacente
const resources = await processTransaction(transaction); const transactionResponse = await processDocumentBundleTransaction(transaction);
const resources = await getResourcesFromTransactionResponse(transactionResponse);
// Paso 2: Obtengo el paciente local (tal como se guardo en la trasnaccion) // Paso 2: Obtengo el paciente local (tal como se guardo en la trasnaccion)
const localPatient = extractResource(resources, PATIENT_RESOURCE_TYPE); const localPatient = extractResource(resources, PATIENT_RESOURCE_TYPE);
// Paso 3: Obtengo el documento IPS local (tal como se guardo en la transacción) // Paso 3: Obtengo el documento IPS local (tal como se guardo en la transacción)
const localIPSDocument = extractResource(resources, IPS_DOCUMENT_RESOURCE_TYPE) 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); const localPatientIdentifier = extractLocalIdentifier(localPatient);
const patientSearchset = await findPatient(token, { identifier: `${localPatientIdentifier.system}|${localPatientIdentifier.value}` });
if (patientSearchset.total == 0) {
throw createError(404, 'Patient does not exists');
}
const nationalPatientId = patientSearchset.entry[0].fullUrl;
const bundleReference = `${config.baseURL}/fhir/Bundle/${localIPSDocument.id}`;
const documentReference = generateDocumentReferenceResource(nationalPatientId, bundleReference);
const createdDocumentReference = await createDocumentReference(token, documentReference);
return res.status(200).json(createdDocumentReference);
} catch (err) { } catch (err) {
next(err); next(err);

View File

@ -14,15 +14,8 @@ function extractNationalIdentifier(patient) {
return identifiers.find(id => id.system === NATIONAL_ID_SYSTEM) || null; return identifiers.find(id => id.system === NATIONAL_ID_SYSTEM) || null;
} }
async function getPatient(token, identifier) {
const response = await findPatient(token, { identifier: identifier });
return Promise.resolve(response.data.entry[0].fullUrl);
}
async function getDocumentReferencesForPatient(token, patient) {
const response = await findDocumentReferenceByPatient(token, patient)
return Promise.resolve(response.data);
}
/** /**
* ITI-67: Find Document References (MHD) * ITI-67: Find Document References (MHD)
* *
@ -52,14 +45,15 @@ async function listDocumentReference(req, res, next) {
].join(',') ].join(',')
); );
// 1. Obtengo el identificador nacional del paciente en forma de referencia // 1. Obtengo el identificador nacional del paciente en forma de referencia
const patientId = await getPatient(token, localPatientIdentifier); const patientSearchset = await findPatient(token, { identifier: localPatientIdentifier });
if (!patientId) { if (!patientSearchset) {
throw createError(422, 'Could not resolve national identifier for the given patient'); throw createError(422, 'Could not resolve national identifier for the given patient');
} }
const patientNationalId = patientSearchset.entry[0].fullUrl
// 2. Obtengo el listado de entradas del indice de atenciones asociadada al id de paciente. // 2. Obtengo el listado de entradas del indice de atenciones asociadada al id de paciente.
const bundle = await getDocumentReferencesForPatient(token, patientId); const documentReferenceSearchset = await findDocumentReferenceByPatient(token, patientNationalId)
res.status(200).json(bundle); res.status(200).json(documentReferenceSearchset);
} catch (err) { } catch (err) {
next(err); next(err);
} }

View File

@ -2,15 +2,6 @@ const createError = require('http-errors');
const axios = require('axios'); const axios = require('axios');
async function getRemoteDocument(url) {
const response = await axios.get(parsedUrl.toString(), {
responseType: 'arraybuffer',
headers: {
Accept: req.headers['accept'] || '*/*',
},
});
return response
}
/** /**
@ -35,7 +26,7 @@ async function getBundleById(req, res, next) {
} catch { } catch {
throw createError(400, 'Invalid document URL'); throw createError(400, 'Invalid document URL');
} }
const response = await getRemoteDocument(url); const response = await getDocumentBundleByUrl(url);
const contentType = response.headers['content-type']; const contentType = response.headers['content-type'];
if (contentType) res.setHeader('Content-Type', contentType); if (contentType) res.setHeader('Content-Type', contentType);

View File

@ -11,8 +11,8 @@ const { findPatient, findPatientById } = require('../services/patient');
async function listPatient(req, res, next) { async function listPatient(req, res, next) {
try { try {
const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope); const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope);
const response = await findPatient(token, req.query); const searchset = await findPatient(token, req.query);
res.status(200).json(response.data); res.status(200).json(searchset);
} catch (err) { } catch (err) {
next(err); next(err);
} }
@ -26,8 +26,8 @@ async function getPatientById(req, res, next) {
try { try {
const { id } = req.params; const { id } = req.params;
const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope); const token = await getBusToken(config.bus.url, config.bus.jwtSecret, config.bus.issuer, config.bus.mpiScope);
const response = await findPatientById(token, id) const patient = await findPatientById(token, id)
res.status(200).json(response.data); res.status(200).json(patient);
} catch (err) { } catch (err) {
next(err); next(err);
} }

View File

@ -18,7 +18,7 @@ async function findDocumentReferenceById(token, id) {
const response = await request.get( const response = await request.get(
URL.parse(`${INDICE_ATENCION_URL}/${id}`) URL.parse(`${INDICE_ATENCION_URL}/${id}`)
); );
return response; return response.data;
} }
@ -27,7 +27,7 @@ async function findDocumentReferenceByUrl(token, id) {
const response = await request.get( const response = await request.get(
URL.parse(id, INDICE_ATENCION_URL) URL.parse(id, INDICE_ATENCION_URL)
); );
return response; return response.data;
} }
/** /**
@ -45,8 +45,8 @@ async function createDocumentReference(token, documentReference) {
URL.parse(INDICE_ATENCION_URL), URL.parse(INDICE_ATENCION_URL),
documentReference documentReference
); );
response.headers['location'] = maskPrivateURL(response.headers['location']); const location = maskPrivateURL(response.headers['location']);
return response; return findDocumentReferenceByUrl(token, location);
} }
/** /**
* Searches DocumentReferences in the document registry by patient subject identifier * Searches DocumentReferences in the document registry by patient subject identifier
@ -65,7 +65,7 @@ async function findDocumentReferenceBySubject(token, subjectSystem, subjectValue
params: { subject: `${subjectSystem}|${subjectValue}` }, params: { subject: `${subjectSystem}|${subjectValue}` },
} }
); );
return response; return response.data;
} }
/** /**
@ -84,7 +84,7 @@ async function findDocumentReferenceByPatient(token, patientId) {
params: { subject: patientId }, params: { subject: patientId },
} }
); );
return response; return response.data;
} }
/** /**
@ -108,7 +108,7 @@ async function searchDocumentReference(token, subject, custodian, type) {
type: `${type.system}|${type.value}`, type: `${type.system}|${type.value}`,
}, },
}); });
return response; return response.data;
} }
module.exports = { module.exports = {
@ -116,6 +116,5 @@ module.exports = {
createDocumentReference, createDocumentReference,
findDocumentReferenceBySubject, findDocumentReferenceBySubject,
findDocumentReferenceByPatient, findDocumentReferenceByPatient,
searchDocumentReference, searchDocumentReference
findDocumentReferenceByUrl
}; };

View File

@ -0,0 +1,43 @@
const config = require("../config");
const axios = require('axios');
const HAPI_FHIR_SERVER_URL = config.fhir.url;
/**
* Procesa una transacción y devuelve los recursos persistidos en el servidor HAPI FHIR
* @param {*} transactionBundle
* @returns
*/
async function processDocumentBundleTransaction(transactionBundle) {
const response = await axios.post(HAPI_FHIR_SERVER_URL, transactionBundle, {
headers: { 'Content-Type': 'application/fhir+json' },
});
return response.data;
}
async function getResourceByUrl(url) {
const response = await axios.get(
URL.parse(url, HAPI_FHIR_SERVER_URL)
);
return response.data;
}
async function getDocumentBundleByUrl(url) {
const response = await axios.get(parsedUrl.toString(), {
responseType: 'arraybuffer',
headers: {
Accept: req.headers['accept'] || '*/*',
},
});
return response.data
}
module.exports = {
processDocumentBundleTransaction,
getResourceByUrl
}

View File

@ -24,8 +24,8 @@ async function createPatient(token, patient) {
URL.parse(MPI_URL), URL.parse(MPI_URL),
patient patient
); );
response.headers['location'] = maskPrivateURL(response.headers['location']); const location = maskPrivateURL(response.headers['location']);
return response; return findPatientByUrl(token, location);
} }
@ -35,7 +35,7 @@ async function updatePatient(token, patient, id) {
URL.parse(`${MPI_URL}/${id}`), URL.parse(`${MPI_URL}/${id}`),
patient patient
); );
return response; return response.data;
} }
/** /**
@ -83,7 +83,7 @@ async function findPatient(token, criteria = {}) {
return l; return l;
}); });
} }
return response; return response.data;
} }
async function findPatientByUrl(token, url) { async function findPatientByUrl(token, url) {
@ -91,7 +91,7 @@ async function findPatientByUrl(token, url) {
const response = await request.get( const response = await request.get(
URL.parse(url, MPI_URL) URL.parse(url, MPI_URL)
); );
return response; return response.data;
} }
/** /**
@ -106,7 +106,7 @@ async function findPatientById(token, id) {
const response = await request.get( const response = await request.get(
URL.parse(`${MPI_URL}/${id}`) URL.parse(`${MPI_URL}/${id}`)
); );
return response; return response.data;
} }
/** /**
@ -140,7 +140,7 @@ async function findPatientByMatch(token, patient, count) {
URL.parse('$match', MPI_URL), URL.parse('$match', MPI_URL),
parameters parameters
); );
return response; return response.data;
} }
module.exports = { createPatient, findPatientById, findPatientByMatch, findPatientByUrl, updatePatient, findPatient }; module.exports = { createPatient, findPatientById, findPatientByMatch, updatePatient, findPatient };