Created
November 8, 2018 01:10
-
-
Save TimMensch/ce75d232096f2cfd623cf28fd1da160e to your computer and use it in GitHub Desktop.
A slightly more complex counterexample to the "Functional River" that would be much uglier in promise form.
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
// Easier to follow. Equally easy to test -- and with one fewer function in need of testing. | |
import { userInfo } from "os"; | |
const Promise = require("bluebird"); | |
const { hashStringAsync } = Promise.promisifyAll(require("./lib/crypto")); | |
const { logEventAsync } = Promise.promisifyAll(require("./lib/log")); | |
const { openAsync } = Promise.promisifyAll(require("./lib/db")); | |
const { TimeoutError, ValidationError, NotFoundError } = require("./errors"); | |
const _openHandle = openAsync(); // FYI: Promises include memoization (caching) built into same API | |
module.exports = { auth }; | |
/* auth is our main function */ | |
async function auth({ | |
username, | |
password, | |
_onTimeoutError = errorHandler, | |
_onNotFoundError = errorHandler, | |
_onValidationError = errorHandler, | |
}) { | |
try { | |
// No more reusing "password" as "hashed password". | |
// Still starting the hash ASAP and not waiting for it. | |
const hashedPasswordPromise = hashStringAsync(password); | |
authValidate({ username, password }); | |
const { users, admin } = await getModels(); | |
const user = await users.findOneAsync({ | |
username, | |
// I would have renamed "password" here; bad form to have a field named | |
// password that's expecting a hash. | |
password: await hashedPasswordPromise, // Deferred await to when we need it. | |
}); | |
// Inventing a side tributary: If a user is an admin, then grab their admin | |
// permissions. | |
if (user.admin) { | |
const admin = await admin.findOneAsync({ id: user.id }); | |
user.adminPermissions = admin.permissions; | |
} | |
await userFound(user); | |
return userInfo; | |
} catch (e) { | |
// Left this in because Bluebird's extension for matching errors is cute. | |
Promise.reject(e) | |
.catch(TimeoutError, _onTimeoutError) | |
.catch(NotFoundError, _onNotFoundError) | |
.catch(ValidationError, _onValidationError) | |
.catch(errorHandler); | |
} | |
} | |
function authValidate({ username, password }) { | |
if (!username || username.length < 4) { | |
throw new ValidationError("Invalid username. Required, 4 char minimum."); | |
} | |
if (!password || password.length < 4) { | |
throw new ValidationError("Invalid password. Required, 4 char minimum."); | |
} | |
} | |
function userFound(results) { | |
if (results) return; | |
throw new NotFoundError("No users matched. Login failed"); | |
} | |
function getModels() { | |
return _openHandle.then(({ models }) => models); | |
} | |
function errorHandler(err) { | |
console.error("Failed auth!", err); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment