Skip to content

Commit

Permalink
Merge pull request #202 from mcode/add_search_parameters
Browse files Browse the repository at this point in the history
Add search parameters
  • Loading branch information
dmendelowitz authored Nov 27, 2023
2 parents 939155f + 0c09fd7 commit 47078f6
Show file tree
Hide file tree
Showing 20 changed files with 53 additions and 193 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@
"object-curly-newline": ["error", {
"ObjectPattern": { "minProperties": 5 }
}],
"no-underscore-dangle": ["error", { "allow": ["__get__", "__set__"] }]
"no-underscore-dangle": ["error", { "allow": ["__get__", "__set__","_include"] }]
}
}
8 changes: 6 additions & 2 deletions src/extractors/BaseFHIRExtractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@ const { getPatientFromContext } = require('../helpers/contextUtils');
const logger = require('../helpers/logger');

class BaseFHIRExtractor extends Extractor {
constructor({ baseFhirUrl, requestHeaders, version, resourceType }) {
constructor({
baseFhirUrl, requestHeaders, version, resourceType, searchParameters = {},
}) {
super();
this.resourceType = resourceType;
this.version = version;
this.baseFHIRModule = new BaseFHIRModule(baseFhirUrl, requestHeaders);
this.searchParameters = searchParameters;
}

updateRequestHeaders(newHeaders) {
Expand All @@ -21,7 +24,8 @@ class BaseFHIRExtractor extends Extractor {
// NOTE: Async because other extractors that extend this may need to make async lookups in the future
async parametrizeArgsForFHIRModule({ context }) {
const patient = getPatientFromContext(context);
return { patient: patient.id };

return { ...this.searchParameters, patient: patient.id };
}
/* eslint-enable class-methods-use-this */

Expand Down
6 changes: 4 additions & 2 deletions src/extractors/FHIRAdverseEventExtractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ const logger = require('../helpers/logger');
const BASE_STUDY = ''; // No base study specified

class FHIRAdverseEventExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version, study }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({
baseFhirUrl, requestHeaders, version, study, searchParameters,
}) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'AdverseEvent';
this.study = study || BASE_STUDY;
}
Expand Down
15 changes: 2 additions & 13 deletions src/extractors/FHIRAllergyIntoleranceExtractor.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

const BASE_CLINICAL_STATUS = 'active';

class FHIRAllergyIntoleranceExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version, clinicalStatus }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'AllergyIntolerance';
this.clinicalStatus = clinicalStatus || BASE_CLINICAL_STATUS;
}

// In addition to default parametrization, add clinical status
async parametrizeArgsForFHIRModule({ context }) {
const paramsWithID = await super.parametrizeArgsForFHIRModule({ context });
return {
...paramsWithID,
'clinical-status': this.clinicalStatus,
};
}
}

Expand Down
16 changes: 2 additions & 14 deletions src/extractors/FHIRConditionExtractor.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

const BASE_CATEGORIES = 'problem-list-item';

class FHIRConditionExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version, category }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'Condition';
this.category = category || BASE_CATEGORIES;
}

// In addition to default parametrization, add category
async parametrizeArgsForFHIRModule({ context }) {
const paramsWithID = await super.parametrizeArgsForFHIRModule({ context });
return {
...paramsWithID,
category: this.category,
};
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/extractors/FHIRDocumentReferenceExtractor.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

class FHIRDocumentReferenceExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'DocumentReference';
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/extractors/FHIREncounterExtractor.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

class FHIREncounterExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'Encounter';
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/extractors/FHIRMedicationOrderExtractor.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

class FHIRMedicationOrderExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'MedicationOrder';
}
}
Expand Down
17 changes: 2 additions & 15 deletions src/extractors/FHIRMedicationRequestExtractor.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

const BASE_STATUSES = ''; // No status specified, returns all statuses (on-hold, completed, stopped, active)

class FHIRMedicationRequestExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version, status }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'MedicationRequest';
this.status = status || BASE_STATUSES;
}

// In addition to default parametrization, add status if specified
async parametrizeArgsForFHIRModule({ context }) {
const paramsWithID = await super.parametrizeArgsForFHIRModule({ context });
// Only add status to parameters if it has been specified
return {
...paramsWithID,
...(this.status && { status: this.status }),
};
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/extractors/FHIRMedicationStatementExtractor.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

class FHIRMedicationStatementExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'MedicationStatement';
}
}
Expand Down
16 changes: 2 additions & 14 deletions src/extractors/FHIRObservationExtractor.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,9 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

const BASE_CATEGORIES = 'laboratory,vital-signs,social-history,LDA,core-characteristics';

class FHIRObservationExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version, category }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'Observation';
this.category = category || BASE_CATEGORIES;
}

// In addition to default parametrization, add category
async parametrizeArgsForFHIRModule({ context }) {
const paramsWithID = await super.parametrizeArgsForFHIRModule({ context });
return {
...paramsWithID,
category: this.category,
};
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/extractors/FHIRPatientExtractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');
const { maskPatientData } = require('../helpers/patientUtils.js');

class FHIRPatientExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version, mask = [] }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({
baseFhirUrl, requestHeaders, version, mask = [], searchParameters,
}) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'Patient';
this.mask = mask;
}
Expand Down
4 changes: 2 additions & 2 deletions src/extractors/FHIRProcedureExtractor.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const { BaseFHIRExtractor } = require('./BaseFHIRExtractor');

class FHIRProcedureExtractor extends BaseFHIRExtractor {
constructor({ baseFhirUrl, requestHeaders, version }) {
super({ baseFhirUrl, requestHeaders, version });
constructor({ baseFhirUrl, requestHeaders, version, searchParameters }) {
super({ baseFhirUrl, requestHeaders, version, searchParameters });
this.resourceType = 'Procedure';
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/helpers/patientUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function maskPatientData(bundle, mask, maskAll = false) {
// get Patient resource from bundle
const patient = fhirpath.evaluate(
bundle,
'Bundle.entry.where(resource.resourceType=\'Patient\').resource,first()',
'Bundle.entry.where(resource.resourceType=\'Patient\').resource.first()',
)[0];

const validFields = [
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/schemas/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@
"title": "Type",
"type": "string"
},
"searchParameters": {
"title": "Search Parameters",
"type": "object"
},
"mask": {
"title": "Masked Fields",
"oneOf": [
Expand Down
16 changes: 14 additions & 2 deletions test/extractors/BaseFHIRExtractor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ const MOCK_URL = 'http://localhost';
const MOCK_REQUEST_HEADERS = {
Accept: 'application/json',
};
const MOCK_SEARCH_PARAMS = { _include: 'Condition::subject',
category: 'problem-list-item',
status: 'final' };
const MOCK_RESOURCE_TYPE = 'Condition';
const MOCK_PATIENT_MRN = 'EXAMPLE-MRN';
const MOCK_CONTEXT = {
Expand All @@ -21,7 +24,7 @@ const MOCK_CONTEXT = {
};

// Create extractor and destructure to mock responses on modules
const baseFHIRExtractor = new BaseFHIRExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_REQUEST_HEADERS, resourceType: MOCK_RESOURCE_TYPE });
const baseFHIRExtractor = new BaseFHIRExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_REQUEST_HEADERS, resourceType: MOCK_RESOURCE_TYPE, searchParameters: MOCK_SEARCH_PARAMS });
const { baseFHIRModule } = baseFHIRExtractor;

// Spies for mocking
Expand All @@ -30,7 +33,10 @@ const moduleRequestHeadersSpy = jest.spyOn(baseFHIRModule, 'updateRequestHeaders

// Ensure that data is returned for condition
when(baseFHIRModuleSearchSpy)
.calledWith('Condition', { patient: examplePatientBundle.entry[0].resource.id })
.calledWith('Condition', { patient: examplePatientBundle.entry[0].resource.id,
_include: MOCK_SEARCH_PARAMS._include,
category: MOCK_SEARCH_PARAMS.category,
status: MOCK_SEARCH_PARAMS.status })
.mockReturnValue(exampleConditionBundle);

// Tests
Expand All @@ -47,6 +53,12 @@ describe('BaseFhirExtractor', () => {
expect(baseFHIRModuleSearchSpy).not.toHaveBeenCalled();
expect(paramsBasedOnContext).toHaveProperty('patient');
expect(paramsBasedOnContext.patient).toEqual(MOCK_CONTEXT.entry[0].resource.id);
expect(paramsBasedOnContext).toHaveProperty('_include');
expect(paramsBasedOnContext._include).toEqual(MOCK_SEARCH_PARAMS._include);
expect(paramsBasedOnContext).toHaveProperty('category');
expect(paramsBasedOnContext.category).toEqual(MOCK_SEARCH_PARAMS.category);
expect(paramsBasedOnContext).toHaveProperty('status');
expect(paramsBasedOnContext.status).toEqual(MOCK_SEARCH_PARAMS.status);
});

test('parametrizeArgsForFHIRModule throws an error if context has no relevant ID', async () => {
Expand Down
31 changes: 0 additions & 31 deletions test/extractors/FHIRAllergyIntoleranceExtractor.test.js
Original file line number Diff line number Diff line change
@@ -1,45 +1,14 @@
const rewire = require('rewire');
const { FHIRAllergyIntoleranceExtractor } = require('../../src/extractors/FHIRAllergyIntoleranceExtractor.js');

const FHIRAllergyIntoleranceExtractorRewired = rewire('../../src/extractors/FHIRAllergyIntoleranceExtractor.js');
const MOCK_URL = 'http://example.com';
const MOCK_HEADERS = {};
const MOCK_MRN = '123456789';
const MOCK_CLINICAL_STATUS = 'status1,status2';
const MOCK_CONTEXT = {
resourceType: 'Bundle',
entry: [
{
fullUrl: 'context-url',
resource: { resourceType: 'Patient', id: MOCK_MRN },
},
],
};

const extractor = new FHIRAllergyIntoleranceExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS });
const extractorWithClinicalStatus = new FHIRAllergyIntoleranceExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS, clinicalStatus: MOCK_CLINICAL_STATUS });
const baseClinicalStatus = FHIRAllergyIntoleranceExtractorRewired.__get__('BASE_CLINICAL_STATUS');

describe('FHIRAllergyIntoleranceExtractor', () => {
describe('Constructor', () => {
test('sets resourceType as AllergyIntolerance', () => {
expect(extractor.resourceType).toEqual('AllergyIntolerance');
});

test('sets clinical status based on BASE_CLINICAL_STATUS', () => {
expect(extractor.clinicalStatus).toEqual(baseClinicalStatus);
});

test('sets clinical status if provided', () => {
expect(extractorWithClinicalStatus.clinicalStatus).toEqual(MOCK_CLINICAL_STATUS);
});
});

describe('parametrizeArgsForFHIRModule', () => {
test('should add category to param values', async () => {
const params = await extractor.parametrizeArgsForFHIRModule({ context: MOCK_CONTEXT });
expect(params).toHaveProperty('clinical-status');
expect(params['clinical-status']).toEqual(baseClinicalStatus);
});
});
});
22 changes: 0 additions & 22 deletions test/extractors/FHIRConditionExtractor.test.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,15 @@
const rewire = require('rewire');
const { FHIRConditionExtractor } = require('../../src/extractors/FHIRConditionExtractor.js');
const MOCK_CONTEXT = require('./fixtures/context-with-patient.json');

const FHIRConditionExtractorRewired = rewire('../../src/extractors/FHIRConditionExtractor');
const MOCK_URL = 'http://example.com';
const MOCK_HEADERS = {};
const MOCK_CATEGORIES = 'category1,category2';


const extractor = new FHIRConditionExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS });
const extractorWithCategories = new FHIRConditionExtractor({ baseFhirUrl: MOCK_URL, requestHeaders: MOCK_HEADERS, category: MOCK_CATEGORIES });
const baseCategories = FHIRConditionExtractorRewired.__get__('BASE_CATEGORIES');

describe('FHIRConditionExtractor', () => {
describe('Constructor', () => {
test('sets resourceType to Condition', () => {
expect(extractor.resourceType).toEqual('Condition');
});

test('sets category based on BASE_CATEGORIES if not provided', () => {
expect(extractor.category).toEqual(baseCategories);
});

test('sets category if provided', () => {
expect(extractorWithCategories.category).toEqual(MOCK_CATEGORIES);
});
});

describe('parametrizeArgsForFHIRModule', () => {
test('should add category to param values', async () => {
const params = await extractor.parametrizeArgsForFHIRModule({ context: MOCK_CONTEXT });
expect(params).toHaveProperty('category');
expect(params.category).toEqual(baseCategories);
});
});
});
Loading

0 comments on commit 47078f6

Please sign in to comment.