diff --git a/service/package.json b/service/package.json index fe36faf..c59dfee 100644 --- a/service/package.json +++ b/service/package.json @@ -13,6 +13,7 @@ "db:reset": "ts-node ./scripts/dbSetup.ts reset", "db:setup": "ts-node ./scripts/dbSetup.ts create", "db:loadBundle": "ts-node ./scripts/dbSetup.ts loadBundle", + "loadBundles": "ts-node ./scripts/loadBundles.ts", "lint": "eslint \"./src/**/*.{js,ts}\"", "lint:fix": "eslint \"./src/**/*.{js,ts}\" --fix", "prettier": "prettier --check \"./src/**/*.{js,ts}\"", diff --git a/service/scripts/loadBundles.ts b/service/scripts/loadBundles.ts new file mode 100644 index 0000000..887169c --- /dev/null +++ b/service/scripts/loadBundles.ts @@ -0,0 +1,150 @@ +import * as fs from 'fs'; +import path from 'path'; + +const SERVER_URL = 'https://abacus-demo.c3ib.org/mrs/4_0_1'; +//const SERVER_URL = 'http://localhost:3000/4_0_1'; +const ECQM_CONTENT_PATH = '/Users/hossenlopp/Downloads/May 2024 Connectathon'; +const MEASURES_PATH = path.join(ECQM_CONTENT_PATH); + +function getBundlePaths(): string[] { + const filePaths: string[] = []; + + fs.readdirSync(MEASURES_PATH, { withFileTypes: true }).forEach(ent => { + // if this item is a directory, look for .json files under it + if (ent.isDirectory()) { + fs.readdirSync(path.join(MEASURES_PATH, ent.name), { withFileTypes: true }).forEach(subEnt => { + if (!subEnt.isDirectory() && subEnt.name.endsWith('.json')) { + filePaths.push(path.join(MEASURES_PATH, ent.name, subEnt.name)); + } else if (subEnt.isDirectory()) { + fs.readdirSync(path.join(MEASURES_PATH, ent.name, subEnt.name), { withFileTypes: true }).forEach( + subSubEnt => { + if (!subSubEnt.isDirectory() && subSubEnt.name.endsWith('.json')) { + filePaths.push(path.join(MEASURES_PATH, ent.name, subEnt.name, subSubEnt.name)); + } + } + ); + } + }); + } + }); + + return filePaths; +} + +function loadBundle(path: string): fhir4.Bundle | null { + const bundle = JSON.parse(fs.readFileSync(path, 'utf8')); + if (bundle.resourceType === 'Bundle') { + return bundle as fhir4.Bundle; + } else { + console.warn(`${path} is not a Bundle`); + return null; + } +} + +async function putLibraries(bundle: fhir4.Bundle) { + const libraries: fhir4.Library[] = bundle.entry + ?.filter(entry => entry.resource?.resourceType === 'Library') + .map(entry => entry.resource as fhir4.Library) as fhir4.Library[]; + /* + console.log(`Fixing refs in ${measure?.id}`); + console.log(measure.relatedArtifact?.length); + measure.relatedArtifact?.forEach(ra => { + //console.log(ra); + if (ra.resource) { + if (!ra.resource.startsWith('http')) { + console.log(` replacing ${ra.resource}`); + ra.resource = 'http://ecqi.healthit.gov/ecqms/' + ra.resource; + } + } + }); + */ + for (const library of libraries) { + try { + console.log(` PUT ${SERVER_URL}/Library/${library.id}`); + + const resp = await fetch(`${SERVER_URL}/Library/${library.id}`, { + method: 'PUT', + body: JSON.stringify(library), + headers: { + 'Content-Type': 'application/json+fhir' + } + }); + console.log(` ${resp.status}`); + } catch (e) { + console.error(e); + } + } +} + +async function fixAndPUTMeasure(bundle: fhir4.Bundle) { + const measure: fhir4.Measure = bundle.entry?.find(entry => entry.resource?.resourceType === 'Measure') + ?.resource as fhir4.Measure; + /* + console.log(`Fixing refs in ${measure?.id}`); + console.log(measure.relatedArtifact?.length); + measure.relatedArtifact?.forEach(ra => { + //console.log(ra); + if (ra.resource) { + if (!ra.resource.startsWith('http')) { + console.log(` replacing ${ra.resource}`); + ra.resource = 'http://ecqi.healthit.gov/ecqms/' + ra.resource; + } + } + }); + */ + try { + console.log(` PUT ${SERVER_URL}/Measure/${measure.id}`); + + const resp = await fetch(`${SERVER_URL}/Measure/${measure.id}`, { + method: 'PUT', + body: JSON.stringify(measure), + headers: { + 'Content-Type': 'application/json+fhir' + } + }); + console.log(` ${resp.status}`); + } catch (e) { + console.error(e); + } +} + +async function transactBundle(bundle: fhir4.Bundle) { + if (bundle.entry) { + for (const entry of bundle.entry) { + if (entry.request?.method === 'POST') { + entry.request.method = 'PUT'; + } + } + } + + try { + console.log(` POST ${SERVER_URL}`); + + const resp = await fetch(`${SERVER_URL}`, { + method: 'POST', + body: JSON.stringify(bundle), + headers: { + 'Content-Type': 'application/json+fhir' + } + }); + console.log(` ${resp.status}`); + } catch (e) { + console.error(e); + } +} + +async function loadBundles(bundlePaths: string[]) { + for (const path of bundlePaths) { + const bundle = loadBundle(path); + if (bundle) { + console.log('FILE ' + path); + await transactBundle(bundle); + //await fixAndPUTMeasure(bundle); + //await putLibraries(bundle); + } + } +} + +const bundlePaths = getBundlePaths(); + +loadBundles(bundlePaths); diff --git a/service/src/requestSchemas.ts b/service/src/requestSchemas.ts index aa5d30c..58ec260 100644 --- a/service/src/requestSchemas.ts +++ b/service/src/requestSchemas.ts @@ -198,7 +198,8 @@ export const CoreSearchArgs = z // adding _summary for count (https://www.hl7.org/fhir/search.html#_summary) _summary: z.literal('count'), // adding _elements for a comma separated string (https://www.hl7.org/fhir/search.html#_elements) - _elements: z.string() + _elements: z.string(), + _count: z.string() }) .partial() .strict(); diff --git a/service/src/util/queryUtils.ts b/service/src/util/queryUtils.ts index bfae179..2c1ede3 100644 --- a/service/src/util/queryUtils.ts +++ b/service/src/util/queryUtils.ts @@ -34,6 +34,8 @@ export function getMongoQueryFromRequest(query: RequestQuery): Filter { } else if (key === '_elements') { const elements = query[key] as string; mf[key] = elements.split(','); + } else if (key === '_count') { + //blackhole _count for now } else { // Otherwise no parsing necessary mf[key] = query[key];