Skip to content

Instantly share code, notes, and snippets.

@snewell92
Last active October 17, 2017 20:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save snewell92/9e4d0c63b9b823c6599837ede69c29d8 to your computer and use it in GitHub Desktop.
Save snewell92/9e4d0c63b9b823c6599837ede69c29d8 to your computer and use it in GitHub Desktop.
Refactor to task
const crypto = require('crypto');
const util = require('util');
const curry = require('lodash/curry');
const compose = require('lodash/flowRight'); // alias flowRight as compose
const moment = require('moment');
const Task = require('folktale/concurrency/task');
const { fromPromised, waitAll } = require('folktale/concurrency/task');
// isValidForgotPasswordRequest :: ForgotPasswordInput -> boolean
const isValidForgotPasswordRequest = data =>
data != null
&& data.usernameOrEmail != null
&& typeof data.usernameOrEmail == 'string'
&& data.usernameOrEmail.length > 0;
// usernameOrEmailExists :: ForgotPasswordInput -> Task string
const usernameOrEmailExists = data => isValidForgotPasswordRequest(data)
? Task.of(data.usernameOrEmail)
: Task.rejected('No username or email');
// toHex :: Buffer -> string
const toHex = b => b.toString('hex');
// getRandomBytes :: () -> Promise Buffer
const getRandomBytes = util.promisify(crypto.randomBytes);
// getExpiration :: () -> number
const getExpiration = () => moment().add(1, 'h').unix();
// genToken :: () -> Task string
const genToken = curry((n, o) =>
fromPromised(() =>
getRandomBytes(n + o).then(toHex)
)()
);
const paginationHasUser = pagedData =>
pagedData != null && pagedData.total > 0 && pagedData.data.length > 0;
const checkUser = pagedData =>
paginationHasUser(pagedData)
? Task.of(pagedData.data[0])
: Task.rejected('No user found');
// teset data to puke out
let testUser = {
id: 1,
email: 'test@test.com',
username: 'admin',
resetToken: null,
resetTokenExpiration: null
};
const queryUser = un =>
un == 'admin'
? Promise.resolve({ total: 1, data: [ testUser ] })
: Promise.resolve({ total: 0, data: [] });
// simulate db query that returns a promise
// fetchUser :: string -> Task User
const fetchUser = fromPromised(queryUser);
// run fetching user and generating token in parallel,
// to test that out / learn how to use tasks better
// getUsernameAndToken :: (getUser) -> (getToken) -> Task string -> Task [ PagedUser, string ]
const getUsernameAndToken = curry((getUser, getToken, task_un) =>
task_un.chain(un => waitAll([ getUser(un), getToken(5) ])));
// assignToken :: Task [ User, string ] -> Task User
const assignToken = tsk =>
tsk.map(([ user, token ]) => {
user.resetToken = token;
user.resetTokenExpiration = getExpiration();
return user;
});
// userExists :: Task [ PagedUser, token ] -> Task [ User, token ]
const userExists = userAndToken =>
userAndToken.chain(([ pagedUser, token ]) =>
checkUser(pagedUser).map(user => [ user, token ]));
// TODO after successful chainTokenAssign, we can
// email user (call microservice)
// compose it all together!
// f :: ForgotPasswordInput -> (string eventually, for now it is whatever object)
let f = compose(assignToken, userExists, getUsernameAndToken(fetchUser, genToken(30)), usernameOrEmailExists);
// TODO could probably wrap argv access up in some functional code
f({ usernameOrEmail: process.argv.slice(2)[0] })
.run().promise()
.then(user =>
console.log(`Hello user number ${user.id}, your reset token is: ${user.resetToken}\nThis will expire at ${moment(user.resetTokenExpiration).toString()}`
))
.catch(console.error);
@snewell92
Copy link
Author

To run, drop the code in a folder and install, then pass it with a username

npm i --save lodash folktale moment
node fp-test.js
node fp-test.js admin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment