Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ const {
// set bodyLimit to 50mb
function build(opts) {
const app = fastify({ ...opts, bodyLimit: 50 * 1024 * 1024 });
app.addContentTypeParser(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great update here! 👍

['application/json+fhir', 'application/fhir+json'],
{ parseAs: 'string' },
app.getDefaultJsonParser('ignore', 'ignore')
);
app.register(cors, { exposedHeaders: ['content-location', 'expires', 'x-progress', 'retry-after'] });
app.get('/metadata', generateCapabilityStatement);
app.get('/$export', bulkExport);
Expand Down
46 changes: 32 additions & 14 deletions src/services/export.service.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { addPendingBulkExportRequest, findResourceById } = require('../util/mongo.controller');
const { addPendingBulkExportRequest, findResourceById, findResourceByCanonical } = require('../util/mongo.controller');
const supportedResources = require('../util/supportedResources').filter(r => r !== 'ValueSet'); //exclude ValueSet (may be stored but not exported)
const exportQueue = require('../resources/exportQueue');
const { patientAttributePaths } = require('fhir-spec-tools/build/data/patient-attribute-paths');
Expand Down Expand Up @@ -437,15 +437,31 @@ const collectData = async (request, reply) => {

const patientIds = [parameters.subject.split('Patient/')[1]];
// Check for measure resolution - errors if there are any issues with measures passed
const measureArr = Array.isArray(parameters.measureId) ? parameters.measureId : [parameters.measureId];
const measurePromises = measureArr.map(async id => {
const measure = await findResourceById(id, 'Measure');
if (!measure) {
reply.code(404).send(new Error(`Unable to find measure with measureId ${id}`));
const measureArr = Array.isArray(parameters.measureUrl) ? parameters.measureUrl : [parameters.measureUrl];
const measures = [];
for (let url of measureArr) {
const resources = await findResourceByCanonical(url, 'Measure');

if (resources.length > 1) {
reply
.code(400)
.send(
createOperationOutcome(`Multiple versions of ${url} were found.`, { issueCode: 400, severity: 'error' })
);
return;
} else if (resources.length === 1) {
measures.push(resources[0]);
} else {
// return if we cant find a measure
reply.code(404).send(
createOperationOutcome(`Measure with url ${url} not found.`, {
issueCode: 404,
severity: 'error'
})
);
return;
}
return measure;
});
const measures = await Promise.all(measurePromises);
}

const bundles = await Promise.all(
patientIds.map(async id => {
Expand Down Expand Up @@ -491,11 +507,7 @@ function validateCollectDataParams(parameters, reply) {
![
'periodStart',
'periodEnd',
'measureId',
'measureIdentifier',
'measureUrl',
'measureResource',
'measure',
'subject',
'subjectGroup',
'practitioner',
Expand Down Expand Up @@ -523,7 +535,7 @@ function validateCollectDataParams(parameters, reply) {

let unsupportedParams = [];
Object.keys(parameters).forEach(param => {
if (!['periodStart', 'periodEnd', 'measureId', 'subject'].includes(param)) {
if (!['periodStart', 'periodEnd', 'measureUrl', 'subject'].includes(param)) {
unsupportedParams.push(param);
}
});
Expand All @@ -538,6 +550,12 @@ function validateCollectDataParams(parameters, reply) {
);
return false;
}
if (!parameters.measureUrl) {
reply
.code(400)
.send(createOperationOutcome('At least one measureUrl is required.', { issueCode: 400, severity: 'error' }));
return false;
}
return true;
}

Expand Down
18 changes: 18 additions & 0 deletions src/util/mongo.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,23 @@ const findResourceById = async (id, resourceType) => {
return collection.findOne({ id: id }, { projection: { _id: 0 } });
};

/**
* searches the database for the desired resource by canonical url
* @param {*} url canonical of desired resource
* @param {*} resourceType type of desired resource, signifies collection resource is stored in
* @returns the data of the found document
*/
const findResourceByCanonical = async (canonical, resourceType) => {
// break apart version if it exists
const [url, version] = canonical.split('|');
console.log(`Looking for measure ${url} with version ${version}`);
const collection = db.collection(resourceType);

return version
? await collection.find({ url, version }, { projection: { _id: 0 } }).toArray()
: await collection.find({ url }, { projection: { _id: 0 } }).toArray();
};

/**
* searches the database for the one resource based on a mongo query and returns the data
* @param {Object} query the mongo query to use
Expand Down Expand Up @@ -192,6 +209,7 @@ const pushBulkStatusWarning = async (clientId, warning) => {
module.exports = {
findResourcesWithQuery,
findResourceById,
findResourceByCanonical,
findOneResourceWithQuery,
createResource,
removeResource,
Expand Down
11 changes: 6 additions & 5 deletions src/util/serviceUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,16 @@ function gatherParams(method, query, body, reply) {
} else {
acc[e.name].push(e.valueReference);
}
} else if (e.name === 'measureId') {
} else if (e.name === 'measureUrl') {
if (!acc[e.name]) {
acc[e.name] = [e.valueId];
acc[e.name] = [e.valueCanonical];
} else {
acc[e.name].push(e.valueId);
acc[e.name].push(e.valueCanonical);
}
} else {
// For now, all usable params are expected to be stored under one of these fives keys
acc[e.name] = e.valueDate || e.valueString || e.valueId || e.valueCode || e.valueReference;
// For now, all usable params are expected to be stored under one of these six keys
acc[e.name] =
e.valueDate || e.valueString || e.valueId || e.valueCanonical || e.valueCode || e.valueReference;
}
}
return acc;
Expand Down
8 changes: 6 additions & 2 deletions test/fixtures/testMeasure.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"resourceType": "Measure",
"id": "testMeasure",
"library": ["Library/testLibrary"],
"url": "http://example.com/Measure/testMeasure",
"version": "1.0.0",
"library": [
"Library/testLibrary"
],
"contained": [
{
"resourceType": "Library",
Expand Down Expand Up @@ -38,4 +42,4 @@
]
}
]
}
}
8 changes: 6 additions & 2 deletions test/fixtures/testMeasure2.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
{
"resourceType": "Measure",
"id": "testMeasure2",
"library": ["Library/testLibrary"],
"url": "http://example.com/Measure/testMeasure2",
"version": "1.0.1",
"library": [
"Library/testLibrary"
],
"contained": [
{
"resourceType": "Library",
Expand All @@ -26,4 +30,4 @@
]
}
]
}
}
45 changes: 45 additions & 0 deletions test/fixtures/testMeasureV2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"resourceType": "Measure",
"id": "testMeasure",
"url": "http://example.com/Measure/testMeasure",
"version": "2.0.0",
"library": [
"Library/testLibrary"
],
"contained": [
{
"resourceType": "Library",
"id": "effective-data-requirements",
"dataRequirement": [
{
"type": "Encounter",
"profile": [
"http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-observation-clinical-result"
],
"codeFilter": [
{
"path": "status",
"code": [
{
"code": "finished"
}
]
}
]
},
{
"type": "Condition",
"profile": [
"http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-condition-problems-health-concerns"
],
"codeFilter": [
{
"path": "code",
"valueSet": "http://example.com/ValueSet/exampleVS"
}
]
}
]
}
]
}
Loading
Loading