diff --git a/src/application/app.js b/src/application/app.js index d61761c4..1908649c 100644 --- a/src/application/app.js +++ b/src/application/app.js @@ -7,6 +7,7 @@ const { sendEmailNotification, zipErrors } = require('./tools/emailNotifications const { extractDataForPatients } = require('./tools/mcodeExtraction'); const { maskMRN } = require('../helpers/patientUtils'); const { parsePatientIds } = require('../helpers/appUtils'); +const { validateConfig } = require('../helpers/configValidator'); function getConfig(pathToConfig) { // Checks pathToConfig points to valid JSON file @@ -20,7 +21,7 @@ function getConfig(pathToConfig) { function checkInputAndConfig(config, fromDate, toDate) { // Check input args and needed config variables based on client being used - const { patientIdCsvPath } = config; + validateConfig(config); // Check if `fromDate` is a valid date if (fromDate && !moment(fromDate).isValid()) { @@ -31,11 +32,6 @@ function checkInputAndConfig(config, fromDate, toDate) { if (toDate && !moment(toDate).isValid()) { throw new Error('-t/--to-date is not a valid date.'); } - - // Check if there is a path to the MRN CSV within our config JSON - if (!patientIdCsvPath) { - throw new Error('patientIdCsvPath is required in config file'); - } } async function mcodeApp(Client, fromDate, toDate, pathToConfig, pathToRunLogs, debug, allEntries) { diff --git a/src/helpers/configValidator.js b/src/helpers/configValidator.js new file mode 100644 index 00000000..6aca537b --- /dev/null +++ b/src/helpers/configValidator.js @@ -0,0 +1,20 @@ +const Ajv = require('ajv'); +const metaSchema = require('ajv/lib/refs/json-schema-draft-06.json'); +const logger = require('./logger'); +const configSchema = require('./schemas/config.schema.json'); + +const ajv = new Ajv({ logger: false, allErrors: true }); +ajv.addMetaSchema(metaSchema); +const validator = ajv.addSchema(configSchema, 'config'); + +function validateConfig(config) { + logger.debug('Validating config file'); + const valid = validator.validate('config', config); + const errors = ajv.errorsText(validator.errors, { dataVar: 'config' }); + if (!valid) throw new Error(`Error(s) found in config file: ${errors}`); + logger.debug('Config file validated successfully'); +} + +module.exports = { + validateConfig, +}; diff --git a/src/helpers/schemas/config.schema.json b/src/helpers/schemas/config.schema.json index ebe678a7..8df6e9ff 100644 --- a/src/helpers/schemas/config.schema.json +++ b/src/helpers/schemas/config.schema.json @@ -1,6 +1,6 @@ { "$id": "csv-config", - "$schema": "https://json-schema.org/draft/2020-12/schema", + "$schema": "http://json-schema.org/draft-06/schema#", "description": "Schema for mcode-extraction-framework config files", "type": "object", "properties": { diff --git a/test/application/app.test.js b/test/application/app.test.js index 18a2bf8d..86897761 100644 --- a/test/application/app.test.js +++ b/test/application/app.test.js @@ -20,17 +20,19 @@ describe('App Tests', () => { }); describe('checkInputAndConfig', () => { + const config = { patientIdCsvPath: '', extractors: [] }; it('should throw error when fromDate is invalid.', () => { - expect(() => checkInputAndConfig(testConfig, '2020-06-31')).toThrowError('-f/--from-date is not a valid date.'); + expect(() => checkInputAndConfig(config, '2020-06-31')).toThrowError('-f/--from-date is not a valid date.'); }); it('should throw error when toDate is invalid date.', () => { - expect(() => checkInputAndConfig(testConfig, '2020-06-30', '2020-06-31')).toThrowError('-t/--to-date is not a valid date.'); + expect(() => checkInputAndConfig(config, '2020-06-30', '2020-06-31')).toThrowError('-t/--to-date is not a valid date.'); }); - it('should throw error when patientIdCsvPath not provided in config', () => { - expect(() => checkInputAndConfig({})).toThrowError('patientIdCsvPath is required in config file'); + it('should throw error when config is not valid', () => { + expect(() => checkInputAndConfig({})) + .toThrowError('Error(s) found in config file: config should have required property \'patientIdCsvPath\', config should have required property \'extractors\''); }); it('should not throw error when all args are valid', () => { - expect(() => checkInputAndConfig(testConfig, '2020-06-01', '2020-06-30')).not.toThrowError(); + expect(() => checkInputAndConfig(config, '2020-06-01', '2020-06-30')).not.toThrowError(); }); }); }); diff --git a/test/helpers/configValidator.test.js b/test/helpers/configValidator.test.js new file mode 100644 index 00000000..456783a7 --- /dev/null +++ b/test/helpers/configValidator.test.js @@ -0,0 +1,24 @@ +const { validateConfig } = require('../../src/helpers/configValidator.js'); + +describe('validateConfig', () => { + const missingPropertyConfig = { patientIdCsvPath: '' }; + const wrongTypeConfig = { patientIdCsvPath: '', extractors: 12 }; + const wrongFormatConfig = { patientIdCsvPath: '', extractors: [], commonExtractorArgs: { baseFhirUrl: 'wrong' } }; + const validConfig = { patientIdCsvPath: '', extractors: [] }; + + test('Should throw error when config file is missing required property', () => { + expect(() => validateConfig(missingPropertyConfig)).toThrowError('Error(s) found in config file: config should have required property \'extractors\''); + }); + + test('Should throw error when property is of incorrect type', () => { + expect(() => validateConfig(wrongTypeConfig)).toThrowError('Error(s) found in config file: config.extractors should be array'); + }); + + test('Should throw error when property has incorrect format', () => { + expect(() => validateConfig(wrongFormatConfig)).toThrowError('Error(s) found in config file: config.commonExtractorArgs.baseFhirUrl should match format "uri"'); + }); + + test('Should not throw error when config file is valid', () => { + expect(() => validateConfig(validConfig)).not.toThrow(); + }); +});