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 } = require('../services/documentReference'); const { logger } = require('../utils/logger'); const { v4: uuidv4 } = require('uuid'); const { getResourceByUrl, processDocumentBundleTransaction } = require('../services/fhir'); 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' function extractResource(resources, resourceType) { return resources.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]; } function generateDocumentReferenceResource(subjectReference, bundleUrl) { const documentRefernece = { 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: subjectReference }, custodian: { identifier: { system: CUSTODIAN_ID_SYSTEM, value: config.bus.issuer }, }, content: [ { attachment: { url: bundleUrl, contentType: 'application/fhir+json' }, }, ], }; return documentRefernece; } 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); } /** * 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 transactionResponse = await processDocumentBundleTransaction(transaction); const resources = await getResourcesFromTransactionResponse(transactionResponse); // 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) 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) { next(err); } } module.exports = { provideDocumentBundle };