Last active
October 17, 2017 20:40
-
-
Save snewell92/9e4d0c63b9b823c6599837ede69c29d8 to your computer and use it in GitHub Desktop.
Refactor to task
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
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: '[email protected]', | |
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); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To run, drop the code in a folder and install, then pass it with a username