Forked from aackerman/gist:93d86b780ef7e951b59351dcc99af1b1
Created
August 22, 2018 14:10
-
-
Save igorkosta/d14eca3a1603009f1c12cc0f621988d0 to your computer and use it in GitHub Desktop.
Parameter Store 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
'use strict' | |
const EventEmitter = require('events') | |
// default expiry 3 mins | |
const DEFAULT_EXPIRY = 3 * 60 * 1000 | |
const create = ({ keys, ssm, keyPrefix = '', expiryMs = DEFAULT_EXPIRY }) => { | |
let isRefreshing = false | |
if (!keys || !Array.isArray(keys) || keys.length === 0) { | |
throw new Error('Provide a non-empty array of config keys') | |
} | |
if (expiryMs <= 0) { | |
throw new Error('Specify an expiry (ms) greater than 0') | |
} | |
let cache = { | |
expiration: new Date(0), | |
items: {} | |
} | |
let eventEmitter = new EventEmitter() | |
let validate = (keys, params) => { | |
let missing = keys.filter(k => params[k] === undefined) | |
if (missing.length > 0) { | |
throw new Error(`Missing SSM Parameter Store keys: ${missing}`) | |
} | |
} | |
let fetchParameters = async () => { | |
if (process.env.NODE_ENV === 'production') { | |
let req = { | |
Names: keys.map(k => `${keyPrefix}${k}`), | |
WithDecryption: true | |
} | |
let { | |
Parameters | |
} = await ssm.getParameters(req).promise() | |
let params = {} | |
for (let p of Parameters) { | |
let name = p.Name.replace(keyPrefix, '') | |
params[name] = p.Value | |
} | |
return params | |
} else { | |
let params = {} | |
for (let key of keys) { | |
params[key] = process.env[key] | |
} | |
return params | |
} | |
} | |
let refresh = async () => { | |
if (isRefreshing) { return } | |
isRefreshing = true | |
console.log(`Refreshing SSM Parameter Store keys: ${keys}`) | |
try { | |
let params = await fetchParameters() | |
validate(keys, params) | |
console.log(`Successfully refreshed SSM Parameter Store keys: ${keys}`) | |
let now = new Date() | |
cache.expiration = new Date(now.getTime() + expiryMs) | |
cache.items = params | |
eventEmitter.emit('refresh') | |
} finally { | |
isRefreshing = false | |
} | |
} | |
let getValue = (key) => { | |
let now = new Date() | |
if (now >= cache.expiration) { | |
refresh() | |
} | |
return cache.items[key] | |
} | |
let config = { | |
refresh: refresh, | |
onRefresh: listener => eventEmitter.addListener('refresh', listener), | |
onRefreshError: listener => eventEmitter.addListener('refreshError', listener) | |
} | |
let defaultKeys = Object.keys(config) | |
for (let key of keys) { | |
key = key.replace(keyPrefix, '') | |
Object.defineProperty(config, key, { | |
get() { return getValue(key) }, | |
enumerable: true, | |
configurable: false | |
}) | |
} | |
// catch usage of undefined keys | |
if (process.env.NODE_ENV !== 'production') { | |
return new Proxy(config, { | |
get(target, key) { | |
if (keys.includes(key) === false && defaultKeys.includes(key) === false) { | |
throw new Error(`Key '${key}' is not defined in config keys`) | |
} | |
return target[key] | |
} | |
}) | |
} | |
return config | |
} | |
module.exports = { | |
create | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment