Last active
November 1, 2018 09:06
-
-
Save i-am-tom/7bfd08f159f891043902cb589833f3be to your computer and use it in GitHub Desktop.
Prompt users for missing/sensitive config data as and when it's required.
This file contains hidden or 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
// Sometimes, you might want to supply *some* config, but | |
// not necessarily *all*. Maybe your password is really | |
// secret, so you don't want to risk saving it in a config | |
// file. In which case, you'll want to ask the user to enter | |
// it when your script runs. | |
// This interface provides a flexible approach, where | |
// the user is prompted for a missing key when it's | |
// requested - if a particular key isn't needed for a given | |
// run, the user won't need to enter it. Of course, if the | |
// particular key exists in the given config, the user isn't | |
// asked. | |
const Task = require('data.task') | |
const { ReaderT } = require('fantasy-readers') | |
const { traverse } = require('ramda') | |
const { stdin: input, stdout: output } = process | |
// Ask the user a question on the command line. | |
// question :: String -> Task e String | |
const question = text => new Task((_, res) => { | |
const rl = require('readline') | |
.createInterface({ input: process.stdin | |
, output: process.stdout | |
, terminal: false }) | |
return rl.question( | |
`${ text }: `, | |
data => { rl.close() | |
res(data) }) | |
}) | |
// type ReaderTask u e a = ReaderT u (Task e a) | |
const ReaderTask = ReaderT(Task) | |
// Get a config key if it exists, or ask the user if not. | |
// getConfig :: String -> ReaderTask StrMap e String | |
const getConfig = key => | |
ReaderTask.ask.chain( | |
config => config[key] === undefined | |
? ReaderTask(_ => question(key)) | |
: ReaderTask.of(config[key])) | |
// Ask the user for any of the three keys that are missing, | |
// then return all the values to be used. | |
traverse(ReaderTask.of, getConfig, | |
[ 'host', 'username', 'password' ]) | |
// Do whatever fun thing with the values you need: connect | |
// to a database, call an API, whatever. | |
.map(([ host, user, pass ]) => ({ host, user, pass })) | |
// Pass in a potentially incomplete config array, whose | |
// missing values will be populated by user input. | |
.run({ 'host': 'localhost', 'username': 'tom' }) | |
// None of the actions here can fail, so we'll just ignore | |
// that and log the result! | |
.fork(_ => undefined, | |
x => console.log(x)) | |
// $ node transformer.js | |
// password: my-password | |
// { host: 'localhost', user: 'tom', pass: 'my-password' } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment