Skip to content

Commit a9e3e18

Browse files
authored
Merge pull request #158 from mcode/masking-all-option
"all" option for Patient masking now supported in the config
2 parents 806d3bc + ec7ef8c commit a9e3e18

File tree

7 files changed

+47
-9
lines changed

7 files changed

+47
-9
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,18 @@ To mask a property, provide an array of the properties to mask in the `construct
148148
}
149149
```
150150

151+
Alternatively, providing a string with a value of `all` in the `constructorArgs` of the Patient extractor will mask all of the supported properties listed above. The following configuration can be used to mask all properties of the `Patient` resource, rather than listing each individual property:
152+
153+
```bash
154+
{
155+
"label": "patient",
156+
"type": "CSVPatientExtractor",
157+
"constructorArgs": {
158+
"filePath": "./data/patient-information.csv"
159+
"mask": "all"
160+
}
161+
}
162+
```
151163
### Extraction Date Range
152164

153165
The mCODE Extraction Client will extract all data that is provided in the CSV files by default, regardless of any dates associated with each row of data. It is recommended that any required date filtering is performed outside of the scope of this client.

src/extractors/CSVPatientExtractor.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,10 @@ class CSVPatientExtractor extends BaseCSVExtractor {
8787
// 3. Generate FHIR Resources
8888
const bundle = generateMcodeResources('Patient', packagedPatientData);
8989

90-
// mask fields in the patient data if specified in mask array
91-
if (this.mask.length > 0) maskPatientData(bundle, this.mask);
90+
// mask specified fields in the patient data
91+
if (typeof this.mask === 'string' && this.mask === 'all') {
92+
maskPatientData(bundle, [], true);
93+
} else if (this.mask.length > 0) maskPatientData(bundle, this.mask);
9294
return bundle;
9395
}
9496
}

src/extractors/FHIRPatientExtractor.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ class FHIRPatientExtractor extends BaseFHIRExtractor {
1818

1919
async get(argumentObject) {
2020
const bundle = await super.get(argumentObject);
21-
if (this.mask.length > 0) maskPatientData(bundle, this.mask);
21+
// mask specified fields in the patient data
22+
if (typeof this.mask === 'string' && this.mask === 'all') {
23+
maskPatientData(bundle, [], true);
24+
} else if (this.mask.length > 0) maskPatientData(bundle, this.mask);
2225
return bundle;
2326
}
2427
}

src/helpers/configUtils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ ajv.addFormat('email-with-name', {
3434
return emailRegex.test(email.trim().split(' ').pop());
3535
},
3636
});
37+
ajv.addFormat('mask-all', {
38+
type: 'string',
39+
validate: (string) => string === 'all',
40+
});
3741

3842
const validator = ajv.addSchema(configSchema, 'config');
3943

src/helpers/patientUtils.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,9 @@ function getPatientName(name) {
8181
* 'gender','mrn','name','address','birthDate','language','ethnicity','birthsex',
8282
* 'race', 'telecom', 'multipleBirth', 'photo', 'contact', 'generalPractitioner',
8383
* 'managingOrganization', and 'link'
84+
* @param {Boolean} maskAll indicates that all supported fields should be masked, defaults to false
8485
*/
85-
function maskPatientData(bundle, mask) {
86+
function maskPatientData(bundle, mask, maskAll = false) {
8687
// get Patient resource from bundle
8788
const patient = fhirpath.evaluate(
8889
bundle,
@@ -109,7 +110,9 @@ function maskPatientData(bundle, mask) {
109110
];
110111
const masked = extensionArr(dataAbsentReasonExtension('masked'));
111112

112-
mask.forEach((field) => {
113+
const maskingFields = maskAll ? validFields : mask;
114+
115+
maskingFields.forEach((field) => {
113116
if (!validFields.includes(field)) {
114117
throw Error(`'${field}' is not a field that can be masked. Patient will only be extracted if all mask fields are valid. Valid fields include: Valid fields include: ${validFields.join(', ')}`);
115118
}

src/helpers/schemas/config.schema.json

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,18 @@
128128
},
129129
"mask": {
130130
"title": "Masked Fields",
131-
"type": "array",
132-
"items": {
133-
"type": "string"
134-
}
131+
"oneOf": [
132+
{
133+
"type": "string",
134+
"format": "mask-all"
135+
},
136+
{
137+
"type": "array",
138+
"items": {
139+
"type": "string"
140+
}
141+
}
142+
]
135143
}
136144
}
137145
}

test/helpers/patientUtils.test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ describe('PatientUtils', () => {
113113
expect(bundle).toEqual(exampleMaskedPatient);
114114
});
115115

116+
test('bundle should be modified to have dataAbsentReason for all fields when the maskAll flag is provided', () => {
117+
const bundle = _.cloneDeep(examplePatient);
118+
maskPatientData(bundle, [], true);
119+
expect(bundle).toEqual(exampleMaskedPatient);
120+
});
121+
116122
test('should mask gender even if it only had an extension', () => {
117123
const bundle = _.cloneDeep(examplePatient);
118124
delete bundle.entry[0].resource.gender;

0 commit comments

Comments
 (0)