ips-nodo-dominio/bus-gateway/services/documentReference.js
Alejandro Gomez Auad 387ee331ed Add env flag to filter DocumentReference type locally in ITI-67
DOCUMENT_REGISTRY_FILTER_TYPE_LOCALLY, when enabled, skips the `type`
query param on GET /DocumentReference to the document registry and
filters the resulting searchset by type client-side instead, matching
the same system|code / |code / code token semantics used elsewhere.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 22:04:08 +00:00

183 lines
7.2 KiB
JavaScript

const config = require('../config');
const { createBusRequest } = require('../utils/busAuth');
const { default: axios } = require('axios');
const INDICE_ATENCION_URL = config.bus.documentRegistryUrl;
const LOCAL_URL = "https://federador.qa-bus-interoperabilidad.svc.cluster.local:8080/fhir/DocumentReference";
function maskPrivateURL(patientId) {
return patientId.replace(LOCAL_URL, INDICE_ATENCION_URL);
}
async function findDocumentReferenceById(token, id) {
const request = createBusRequest(token);
const response = await request.get(
URL.parse(`${INDICE_ATENCION_URL}/${id}`)
);
return response.data;
}
async function findDocumentReferenceByUrl(token, id) {
const request = createBusRequest(token);
const response = await request.get(
URL.parse(id, INDICE_ATENCION_URL)
);
return response.data;
}
/**
* Posts a DocumentReference to the document registry
* (POST /fhir/DocumentReference).
* @param {string} documentRegistryUrl - Base URL of the document registry service.
* @param {string} token - Bearer access token.
* @param {object} documentReference - FHIR DocumentReference resource to register.
* @returns {Promise<object>} The created DocumentReference resource returned by the registry.
* @todo Agregar system http://federador.msal.gob.ar/uri para armar el identificador del custodian: http://federador.msal.gob.ar/uri|<custodianId>
*/
async function createDocumentReference(token, documentReference) {
const request = createBusRequest(token);
const response = await request.post(
URL.parse(INDICE_ATENCION_URL),
documentReference
);
const location = maskPrivateURL(response.headers['location']);
return findDocumentReferenceByUrl(token, location);
}
/**
* Searches DocumentReferences in the document registry by patient subject identifier
* (GET /fhir/DocumentReference?subject=system|value).
* @param {string} documentRegistryUrl - Base URL of the document registry service.
* @param {string} token - Bearer access token.
* @param {string} subjectSystem - Identifier system of the patient.
* @param {string} subjectValue - Identifier value of the patient.
* @returns {Promise<object>} FHIR Bundle (searchset) with matching DocumentReferences.
*/
async function findDocumentReferenceBySubject(token, subjectSystem, subjectValue) {
const request = createBusRequest(token);
const response = await request.get(
URL.parse(INDICE_ATENCION_URL),
{
params: { subject: `${subjectSystem}|${subjectValue}` },
}
);
return response.data;
}
/**
* Parses a FHIR token search value (`system|code`, `|code` o `code`) into its parts.
* `system === null` indica la forma `|code` (exige coding sin system).
* `system === undefined` indica la forma `code` (sin restricción de system).
* @param {string} token
* @returns {{ system: string|null|undefined, code: string }}
*/
function parseTypeToken(token) {
const pipeIndex = token.indexOf('|');
if (pipeIndex === -1) {
return { system: undefined, code: token };
}
const system = token.slice(0, pipeIndex);
return { system: system === '' ? null : system, code: token.slice(pipeIndex + 1) };
}
/**
* Indica si el CodeableConcept `type` de un DocumentReference matchea el token FHIR dado,
* replicando la semántica estándar de búsqueda por token (`system|code`, `|code` o `code`)
* que el índice de documentos aplicaría del lado del servidor.
* @param {object} resource - DocumentReference resource.
* @param {string} token - Token FHIR de DocumentReference.type.
* @returns {boolean}
*/
function matchesTypeToken(resource, token) {
const { system, code } = parseTypeToken(token);
const codings = resource?.type?.coding ?? [];
return codings.some(coding => {
if (system === null) {
return !coding.system && coding.code === code;
}
if (system !== undefined) {
return coding.system === system && coding.code === code;
}
return coding.code === code;
});
}
/**
* Filtra las entries de un Bundle searchset de DocumentReference por type,
* ajustando `total` a la cantidad de entries resultantes.
* @param {object} bundle - FHIR Bundle (searchset).
* @param {string} type - Token FHIR de DocumentReference.type.
* @returns {object} Bundle filtrado.
*/
function filterBundleByType(bundle, type) {
if (!bundle || !Array.isArray(bundle.entry)) {
return bundle;
}
const entry = bundle.entry.filter(e => matchesTypeToken(e.resource, type));
return { ...bundle, entry, total: entry.length };
}
/**
* Searches DocumentReferences in the document registry by patient internal ID,
* opcionalmente filtrando por tipo de documento
* (GET /fhir/DocumentReference?patient=value[&type=system|code]).
*
* Si `config.bus.filterDocumentTypeLocally` está activo y se pidió `type`, el parámetro
* `type` no se envía al índice de documentos: se piden todos los DocumentReference del
* subject y el filtrado por tipo se hace localmente sobre el Bundle resultante.
* @param {string} documentRegistryUrl - Base URL of the document registry service.
* @param {string} token - Bearer access token.
* @param {string} patientId - Internal patient ID in the document registry.
* @param {string} [type] - Token FHIR (`system|code` o `code`) de DocumentReference.type, ej: `http://loinc.org|60591-5`.
* @returns {Promise<object>} FHIR Bundle (searchset) with matching DocumentReferences.
*/
async function findDocumentReferenceByPatient(token, patientId, type) {
const request = createBusRequest(token);
const filterLocally = Boolean(type) && config.bus.filterDocumentTypeLocally;
const params = { subject: patientId };
if (type && !filterLocally) {
params.type = type;
}
const response = await request.get(
URL.parse(INDICE_ATENCION_URL),
{ params }
);
return filterLocally ? filterBundleByType(response.data, type) : response.data;
}
/**
* Searches DocumentReferences in the document registry filtered by subject, custodian and type
* (GET /fhir/DocumentReference?subject=...&custodian=...&type=...).
* @param {string} documentRegistryUrl - Base URL of the document registry service.
* @param {string} token - Bearer access token.
* @param {{ system: string, value: string }} subject - Patient identifier (TokenParam).
* @param {{ system: string, value: string }} custodian - Custodian identifier (TokenParam).
* @param {{ system: string, value: string }} type - Document type (TokenParam, e.g. { system: 'http://loinc.org', value: '60591-5' }).
* @returns {Promise<object>} FHIR Bundle (searchset) with matching DocumentReferences.
*/
async function searchDocumentReference(token, subject, custodian, type) {
const request = createBusRequest(token);
const response = await request.get(
URL.parse(INDICE_ATENCION_URL),
{
params: {
subject: `${subject.system}|${subject.value}`,
custodian: `${custodian.system}|${custodian.value}`,
type: `${type.system}|${type.value}`,
},
});
return response.data;
}
module.exports = {
findDocumentReferenceById,
createDocumentReference,
findDocumentReferenceBySubject,
findDocumentReferenceByPatient,
searchDocumentReference
};