-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: init function now works properly with default params (#7)
- Loading branch information
1 parent
c41f6ff
commit b97ae2b
Showing
4 changed files
with
140 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,29 @@ | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const yaml = require('js-yaml'); | ||
|
||
const { | ||
isObject, | ||
loadConfiguration, | ||
overrideConfigValuesFromSystemVariables, | ||
} = require('./utils'); | ||
|
||
// Global config | ||
let config; | ||
|
||
/** | ||
* Do a deep merge of two objects. This function will lead to infinite recursion on circular references | ||
* @param target | ||
* @param source | ||
*/ | ||
function mergeDeep(target, source) { | ||
const output = { ...target }; | ||
if (isObject(target) && isObject(source)) { | ||
Object.keys(source).forEach(key => { | ||
if (isObject(source[key])) { | ||
if (!(key in target)) Object.assign(output, { [key]: source[key] }); | ||
else output[key] = mergeDeep(target[key], source[key]); | ||
} else { | ||
Object.assign(output, { [key]: source[key] }); | ||
} | ||
}); | ||
} | ||
return output; | ||
} | ||
|
||
/** | ||
* Load a configuration file recursively. The nested config overrides the previous. | ||
* This function will lead to infinite recursion on circular references | ||
* @param confObj | ||
*/ | ||
function loadConfig(confObj, filePath, configDirectory) { | ||
let resultConfig = {}; | ||
if (confObj.include) { | ||
if (Array.isArray(confObj)) | ||
throw new Error(`${filePath}: include field must be an array!`); | ||
|
||
confObj.include.forEach(confName => { | ||
const subPath = path.join(configDirectory, `app.${confName}.config.yaml`); | ||
const includeConf = yaml.safeLoad(fs.readFileSync(subPath, 'utf8')); | ||
resultConfig = mergeDeep( | ||
resultConfig, | ||
loadConfig(includeConf, subPath, configDirectory) | ||
); | ||
}); | ||
} | ||
return mergeDeep(resultConfig, confObj); | ||
} | ||
|
||
function init({ profile = process.env.NODE_ENV, configDirectory = './' }) { | ||
if (!profile) throw new Error('NODE_ENV not set.'); | ||
function init({ | ||
profile = process.env.NODE_ENV, | ||
configDirectory = process.cwd(), | ||
} = {}) { | ||
if (!profile) | ||
throw new Error( | ||
'No profile was given: set NODE_ENV or pass it as a parameter' | ||
); | ||
|
||
if (config) { | ||
console.warn( | ||
'autoConfig.init was called again. Config will be re-initialized.' | ||
'Config will be re-initialized: autoConfig.init was called again' | ||
); | ||
} | ||
|
||
try { | ||
// Load config | ||
const filePath = path.join(configDirectory, `app.${profile}.config.yaml`); | ||
|
||
const confObj = yaml.safeLoad(fs.readFileSync(filePath, 'utf8')); | ||
config = loadConfig(confObj, filePath, configDirectory); | ||
|
||
overrideConfigValuesFromSystemVariables(config); | ||
delete config.include; | ||
} catch (e) { | ||
throw new Error(`Auto configuration failed. ${e}`); | ||
} | ||
config = loadConfiguration(configDirectory, profile); | ||
overrideConfigValuesFromSystemVariables(config); | ||
delete config.include; | ||
} | ||
|
||
module.exports = { init, getConfig: () => config }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,71 @@ | ||
describe('Test autoConfig', () => { | ||
it('dummy test to setup jest', () => { | ||
expect(true).toBeTruthy(); | ||
const utils = require('../lib/utils'); | ||
|
||
jest.mock('../lib/utils', () => ({ | ||
loadConfiguration: jest.fn(() => ({ | ||
include: [], | ||
})), | ||
overrideConfigValuesFromSystemVariables: jest.fn(), | ||
})); | ||
|
||
describe('test autoConfig', () => { | ||
let autoConfig; | ||
|
||
beforeEach(() => { | ||
jest.isolateModules(() => { | ||
autoConfig = require('../lib/autoConfig'); | ||
}); | ||
}); | ||
|
||
it('should throw error if profile is not defined', () => { | ||
delete process.env.NODE_ENV; | ||
expect(() => autoConfig.init()).toThrowError( | ||
/No profile was given: set NODE_ENV or pass it as a parameter/ | ||
); | ||
process.env.NODE_ENV = 'test'; | ||
}); | ||
|
||
it('should warn if init is called more than once', () => { | ||
const spy = jest.spyOn(console, 'warn'); | ||
spy.mockImplementation(() => {}); | ||
|
||
autoConfig.init(); | ||
autoConfig.init(); | ||
|
||
expect(console.warn).toHaveBeenCalledWith( | ||
'Config will be re-initialized: autoConfig.init was called again' | ||
); | ||
spy.mockRestore(); | ||
}); | ||
|
||
it('should not contain keyword include in the config object', () => { | ||
autoConfig.init(); | ||
expect(autoConfig.getConfig().include).toBeUndefined(); | ||
}); | ||
|
||
it('should configure profile using optional object parameter', () => { | ||
autoConfig.init({ profile: 'mockProfile' }); | ||
expect(utils.loadConfiguration).toHaveBeenCalledWith( | ||
expect.anything(), | ||
'mockProfile' | ||
); | ||
}); | ||
|
||
it('should configure config directory using option object parameter', () => { | ||
autoConfig.init({ profile: 'mockProfile', configDirectory: '/mock/test' }); | ||
expect(utils.loadConfiguration).toHaveBeenCalledWith( | ||
'/mock/test', | ||
'mockProfile' | ||
); | ||
}); | ||
|
||
it('should be able to get the configuration files', () => { | ||
const mockConfig = { | ||
test: 1, | ||
mock: 'mock', | ||
}; | ||
utils.loadConfiguration.mockImplementationOnce(() => mockConfig); | ||
expect(autoConfig.getConfig()).toBeUndefined(); | ||
autoConfig.init(); | ||
expect(autoConfig.getConfig()).toEqual(mockConfig); | ||
}); | ||
}); |