@jepz20/conman
Advanced tools
Comparing version 0.0.4 to 0.0.5
{ | ||
"name": "@jepz20/conman", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "Configuration manager that supports ttl and plugabble sources", | ||
"main": "index.js", | ||
"main": "./src/conman", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "jest", | ||
"coverage": "jest --coverage", | ||
"precommit": "lint-staged", | ||
"lint": "node_modules/.bin/eslint \"**/*.js\" --ignore-path .eslintignore" | ||
}, | ||
@@ -14,3 +17,52 @@ "author": "", | ||
}, | ||
"gitHead": "62d1fe3df9508bdb270869ce4f8f64bfa40d381a" | ||
"gitHead": "747563d5d0f979b87d9bd1a8d3f94bd6790e33ba", | ||
"lint-staged": { | ||
"src/**/*.*": [ | ||
"yarn lint", | ||
"prettier --write", | ||
"git add" | ||
] | ||
}, | ||
"dependencies": { | ||
"app-root-path": "^2.2.1", | ||
"jsonfile": "^5.0.0", | ||
"ramda": "^0.26.1" | ||
}, | ||
"devDependencies": { | ||
"eslint": "^6.1.0", | ||
"eslint-config-airbnb-base": "^14.0.0", | ||
"eslint-config-prettier": "^6.0.0", | ||
"eslint-loader": "1.9.0", | ||
"eslint-plugin-import": "2.18.2", | ||
"eslint-plugin-jest": "^22.15.1", | ||
"eslint-plugin-prettier": "2.7.0", | ||
"husky": "^0.14.3", | ||
"jest": "^24.8.0", | ||
"jest-fetch-mock": "^2.1.2", | ||
"jest-junit": "^7.0.0", | ||
"lint-staged": "^7.3.0", | ||
"prettier": "1.18.2", | ||
"prettier-eslint-cli": "4.7.1" | ||
}, | ||
"eslintConfig": { | ||
"extends": [ | ||
"airbnb-base", | ||
"prettier" | ||
], | ||
"plugins": [ | ||
"jest" | ||
], | ||
"rules": { | ||
"no-underscore-dangle": 0, | ||
"no-use-before-define": 0, | ||
"jest/no-disabled-tests": "warn", | ||
"jest/no-focused-tests": "error", | ||
"jest/no-identical-title": "error", | ||
"jest/prefer-to-have-length": "warn", | ||
"jest/valid-expect": "error" | ||
}, | ||
"env": { | ||
"jest/globals": true | ||
} | ||
} | ||
} |
const { mergeDeepRight } = require('ramda'); | ||
const NodeSelector = require('./helpers/nodeSelector'); | ||
const jsonfile = require('jsonfile'); | ||
const appRoot = require('app-root-path'); | ||
const nodeSelector = require('./helpers/nodeSelector'); | ||
const obfuscate = require('./helpers/obfuscate'); | ||
const SECOND = 1000; | ||
const MINUTE = 60 * SECOND; | ||
const defaultLogger = { | ||
log: console.log, | ||
info: console.log, | ||
debug: console.log, | ||
error: console.log, | ||
warn: console.log | ||
}; | ||
const defaultLogger = console; | ||
const cacheFileName = `${appRoot}/conman.cache.json`; | ||
let isInitialized = false; | ||
const sources = []; | ||
let options = { | ||
// TODO CHANGE THIS TO 5 MINUTES | ||
ttl: 10 * SECOND, | ||
logEnabled: true, | ||
logger: defaultLogger | ||
const defaultOptions = { | ||
ttl: 5 * MINUTE, | ||
logEnabled: false, | ||
logger: defaultLogger, | ||
useFile: true, | ||
cacheFileName | ||
}; | ||
let privateCache = {}; | ||
let conman; | ||
let sources = []; | ||
let options = defaultOptions; | ||
let privateCache; | ||
let ttlInterval; | ||
let selector; | ||
let logger; | ||
function _prepareCacheObject(tree) { | ||
return { | ||
lastModified: new Date().getTime(), | ||
data: tree | ||
}; | ||
} | ||
const _log = opts => (type, ...args) => { | ||
if (opts.logEnabled) { | ||
opts.logger[type](...args); | ||
} | ||
}; | ||
function _writeToFile(cacheObject, opts) { | ||
if (!opts.useFile) { | ||
return Promise.resolve(); | ||
} | ||
logger('log', 'Writing conman cache to file'); | ||
return jsonfile | ||
.writeFile(opts.cacheFileName, cacheObject, { | ||
spaces: 2, | ||
EOL: '\r\n' | ||
}) | ||
.then(res => { | ||
logger( | ||
'log', | ||
`Succesfully wrote conman cache to file ${opts.cacheFileName}` | ||
); | ||
return res; | ||
}) | ||
.catch(error => { | ||
logger( | ||
'error', | ||
`Couldn't write cache to file ${opts.cacheFileName}`, | ||
error | ||
); | ||
}); | ||
} | ||
function _isExpired(cache, opts) { | ||
return new Date().getTime() - cache.lastModified <= opts.ttl; | ||
} | ||
function _readFromFile(opts) { | ||
return jsonfile | ||
.readFile(opts.cacheFileName) | ||
.then(cache => { | ||
if (cache && cache.lastModified && _isExpired(cache, opts)) { | ||
logger('log', `Succesfully read cache from file ${opts.cacheFileName}`); | ||
return cache.data; | ||
} | ||
return null; | ||
}) | ||
.catch(err => { | ||
logger( | ||
'error', | ||
`Could not read cache config file "${opts.cacheFileName}"`, | ||
err | ||
); | ||
return null; | ||
}); | ||
} | ||
function _validateSource(source) { | ||
@@ -37,3 +107,3 @@ if (typeof source.build !== 'function') { | ||
function _ttl() { | ||
function _triggerInterval() { | ||
clearInterval(ttlInterval); | ||
@@ -43,2 +113,3 @@ if (options.ttl <= 0) { | ||
} | ||
ttlInterval = setInterval(build, options.ttl); | ||
@@ -48,5 +119,6 @@ } | ||
function _init(userOptions = {}) { | ||
options = { ...options, ...userOptions }; | ||
options = { ...defaultOptions, ...userOptions }; | ||
logger = _log(options); | ||
isInitialized = true; | ||
selector = new NodeSelector(options); | ||
selector = nodeSelector(logger); | ||
return conman; | ||
@@ -60,3 +132,3 @@ } | ||
} | ||
options.logger.info(`Source "${source.type}" added to conman`); | ||
logger('log', `Source "${source.type}" added to conman`); | ||
sources.push(source); | ||
@@ -66,41 +138,94 @@ return conman; | ||
function get(key) { | ||
function _get(key, _privateCache) { | ||
if (key === undefined) { | ||
return _privateCache; | ||
} | ||
return selector.query(_privateCache, key); | ||
} | ||
function get(keys) { | ||
if (!isInitialized) { | ||
throw new Error('Conman has not been initialize'); | ||
} | ||
if (key === undefined) { | ||
return privateCache; | ||
if (Array.isArray(keys)) { | ||
return keys.map(key => _get(key, privateCache)); | ||
} | ||
return selector.query(privateCache, key); | ||
return _get(keys, privateCache); | ||
} | ||
function build() { | ||
const sourcesTypes = sources.map(({ name, type }) => name || type); | ||
options.logger.info( | ||
function getObfuscate(keys, params) { | ||
return obfuscate(get(keys), params); | ||
} | ||
function _buildSources(_sources) { | ||
const sourcesTypes = _sources.map(({ name, type }) => name || type); | ||
logger( | ||
'log', | ||
`Build triggered for sources: "${sourcesTypes.join()}" at ${new Date().toISOString()}` | ||
); | ||
_ttl(); | ||
return Promise.all(sources.map(source => source.build())) | ||
.then(configs => { | ||
return configs.reduce((acc, config) => { | ||
acc = mergeDeepRight(acc, config); | ||
return acc; | ||
}); | ||
}) | ||
.then(configs => { | ||
options.logger.info( | ||
`Build completed for sources: "${sourcesTypes.join()}" at ${new Date().toISOString()}` | ||
); | ||
return setPrivateCache(configs); | ||
}); | ||
return Promise.all(_sources.map(source => source.build())).then(configs => { | ||
logger( | ||
'log', | ||
`Build completed for sources: "${sourcesTypes.join()}" at ${new Date().toISOString()}` | ||
); | ||
return configs.reduce((acc, config) => { | ||
return mergeDeepRight(acc, config); | ||
}, {}); | ||
}); | ||
} | ||
function setPrivateCache(config) { | ||
function _setPrivateCache(config) { | ||
privateCache = config; | ||
return config; | ||
} | ||
const conman = _init; | ||
function _buildAndSaveCache(_sources) { | ||
return _buildSources(_sources).then(configs => { | ||
return _writeToFile(_prepareCacheObject(configs), options).then(() => | ||
_setPrivateCache(configs) | ||
); | ||
}); | ||
} | ||
function build() { | ||
_triggerInterval(); | ||
// read from file if its the first build | ||
if (!privateCache && options.useFile) { | ||
return _readFromFile(options).then(cache => { | ||
if (!cache) { | ||
return _buildAndSaveCache(sources); | ||
} | ||
return _setPrivateCache(cache); | ||
}); | ||
} | ||
return _buildAndSaveCache(sources); | ||
} | ||
function stop() { | ||
clearInterval(ttlInterval); | ||
} | ||
function reset() { | ||
sources = []; | ||
options = defaultOptions; | ||
isInitialized = false; | ||
ttlInterval = undefined; | ||
privateCache = undefined; | ||
logger = undefined; | ||
clearInterval(ttlInterval); | ||
} | ||
conman = _init; | ||
conman.build = build; | ||
conman.stop = stop; | ||
conman.reset = reset; | ||
conman.addSource = addSource; | ||
conman.get = get; | ||
conman.getObfuscate = getObfuscate; | ||
module.exports = conman; |
@@ -1,3 +0,1 @@ | ||
'use strict'; | ||
/** | ||
@@ -8,4 +6,31 @@ * Node Selector traverses a tree structure and fetches the value of a node, | ||
function nodeSelector(options) { | ||
query(tree, property) { | ||
/** | ||
* Private helper function to iterate over tree recursively. | ||
* | ||
* @param {object} tree | ||
* @param {string} property | ||
* @return {Mixed} | ||
*/ | ||
function _query(tree, property) { | ||
const propertyNameParts = Array.isArray(property) | ||
? property | ||
: property.split('.'); | ||
const name = propertyNameParts[0]; | ||
const value = tree[name]; | ||
if (propertyNameParts.length <= 1) { | ||
return value; | ||
} | ||
// Note that typeof null === 'object' | ||
if (value === null || typeof value !== 'object') { | ||
return undefined; | ||
} | ||
return _query(value, propertyNameParts.slice(1)); | ||
} | ||
function nodeSelector(logger) { | ||
function query(tree, property) { | ||
if (tree === null || tree === undefined) { | ||
@@ -23,6 +48,6 @@ throw new Error( | ||
var value = _query(tree, property); | ||
const value = _query(tree, property); | ||
if (value === undefined) { | ||
options.logger.error('Property "' + property + '" is not defined.'); | ||
logger('log', `Property "${property}" is not defined.`); | ||
} | ||
@@ -34,31 +59,5 @@ | ||
query | ||
} | ||
}; | ||
} | ||
/** | ||
* Private helper function to iterate over tree recursively. | ||
* | ||
* @param {object} tree | ||
* @param {string} property | ||
* @return {Mixed} | ||
*/ | ||
function _query(tree, property) { | ||
let propertyNameParts = Array.isArray(property) | ||
? property | ||
: property.split('.'), | ||
name = propertyNameParts[0], | ||
value = tree[name]; | ||
if (propertyNameParts.length <= 1) { | ||
return value; | ||
} | ||
// Note that typeof null === 'object' | ||
if (value === null || typeof value !== 'object') { | ||
return undefined; | ||
} | ||
return _query(value, propertyNameParts.slice(1)); | ||
} | ||
module.exports = nodeSelector; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
262580
10
669
2
1
213
3
14
1
+ Addedapp-root-path@^2.2.1
+ Addedjsonfile@^5.0.0
+ Addedramda@^0.26.1
+ Addedapp-root-path@2.2.1(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedjsonfile@5.0.0(transitive)
+ Addedramda@0.26.1(transitive)
+ Addeduniversalify@0.1.2(transitive)