Merge branch 'T-954157790482088-User-Can-Create-A-Profile'

This commit is contained in:
Okechi Onyeje 2019-01-02 22:10:19 -05:00
commit b3e4dfb3ab
10 changed files with 859 additions and 727 deletions

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
9.11.1

1408
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -31,11 +31,13 @@
}
},
"dbPath": {
"users": "users"
"users": "users",
"profiles": "profiles"
},
"loki" : {
"verificationTTL": 300000,
"verificationTTLClear": 1800000
}
},
"defaultProfileName": "Business Profile"
}

View File

@ -1,4 +1,5 @@
module.exports = {
//Auth erors
AUTH_INVALID_EMAIL: 'auth/invalid-email',
AUTH_INVALID_PHONE_NUMBER: 'auth/invalid-phone-number',
AUTH_USER_NOT_FOUND: 'auth/user-not-found',
@ -8,8 +9,17 @@ module.exports = {
AUTH_PHONE_ALREADY_EXISTS: 'auth/phone-number-already-exists',
AUTH_PHONE_NOT_CACHED: 'auth/phone-number-not-cached',
AUTH_VERIFICATION_CODE_MISMATCH: 'auth/phone-verification-code-mismatch',
AUTH_INVALID_SESSION_ID: 'auth/invalid-session-id',
AUTH_UNAUTHORIZED_ACCESS: 'auth/unauthorized-access-into-api',
AUTH_UNABLE_TO_EXTRACT_SESSION_ID_FROM_AUTH_HEADER: 'auth/unable-to-extract-session-id-from-auth-header',
//App Errors
APP_NETWORK_ERROR: 'app/network-error',
APP_NETWORK_TIMEOUT: 'app/network-timeout',
APP_UNABLE_TO_PARSE_RESPONSE: 'app/unable-to-parse-response',
MISSING_BODY_ATTRIBUTES: 'request/missing-body-attributes'
APP_FIREBASE_DATABASE_ERROR: 'app/firebase-database-error',
//Request Errors
MISSING_BODY_ATTRIBUTES: 'request/missing-body-attributes',
//Profile Errors
MAX_NUMBER_OF_PROFILES_REACHED: 'profile/max-number-of-profiles-are-being-used',
NO_PROFILE_FOUND_FOR_NUMBER: 'profile/no-profile-found-for-number'
};

View File

@ -1,5 +1,6 @@
const AuthHandlers = require('../ups/auth/handlers');
const UserHandlers = require('../ups/users/handlers');
const ProfileHandlers = require('../ups/profiles/handlers');
/**
* Main router for our application, include all routes here. No logic should be added in this file.
@ -17,6 +18,9 @@ function router(app) {
// Users Endpoints
app.get('/users/by/phone', UserHandlers.findNetworkByPhone);
// Profile Endpoints
app.post('/profiles/:profileNumber', ProfileHandlers.updateUserProfile);
}
module.exports = router;

View File

@ -106,6 +106,22 @@ function createNewSessionId(tagferId) {
return sessionId;
}
/**
* Gets the loki session containg the user's tagferId on the server
* @param {string} id
* @returns {object} Loki Session object containg user's tagferId or error if id param passed is invalid
* @throws AUTH_INVALID_SESSION_ID
*/
function getSession(id) {
const sessions = loki.getCollection("sessions");
var session = sessions.by('id', id)
if (session) {
return session;
} else {
throw errors.AUTH_INVALID_SESSION_ID;
}
}
/**
* Create new verification code for the phone number
*/
@ -166,6 +182,7 @@ module.exports = {
signinWithEmail,
createNewUser,
createNewSessionId,
getSession,
resetPassword
};

View File

@ -1,6 +1,9 @@
const dao = require('./dao');
const authDao = require('./dao');
const profileDao = require('./../profiles/dao');
const utils = require('../utils/utils');
const errors = require('../../config/errors');
const appConfig = require('../../config/app.json');
const http = require('../../config/http');
// Handlers
/**
@ -14,7 +17,7 @@ function doesAttributeExist(req, res) {
if (!utils.isAppSecretValid(req,res)) {
return;
}
var promise = email? dao.doesEmailExist(email) : dao.doesTagferIdExist(tagferId.toLowerCase());
var promise = email? authDao.doesEmailExist(email) : authDao.doesTagferIdExist(tagferId.toLowerCase());
promise.then((result) => res.json({ result }) ).catch(error => res.json(error));
}
@ -32,9 +35,9 @@ function sendPhoneCode(req, res) {
}
const phoneNumber = req.body.phoneNumber;
dao.doesPhoneExist(phoneNumber).then( result => {
authDao.doesPhoneExist(phoneNumber).then( result => {
if (result === false) {
dao.sendVerificationCode(phoneNumber, status => res.json(status));
authDao.sendVerificationCode(phoneNumber, status => res.json(status));
} else{
throw { error: errors.AUTH_PHONE_ALREADY_EXISTS };
}
@ -54,7 +57,7 @@ function verifyPhoneCode(req, res) {
const phoneNumber = req.body.phoneNumber;
const code = req.body.code;
res.json(dao.isVerificationCodeCorrect(phoneNumber, code));
res.json(authDao.isVerificationCodeCorrect(phoneNumber, code));
}
/**
@ -73,13 +76,13 @@ function signin(req, res) {
var promise;
if (email) {
promise = dao.signinWithEmail(email, password);
promise = authDao.signinWithEmail(email, password);
} else {
promise = dao.signinWithTagferId(tagferId, password);
promise = authDao.signinWithTagferId(tagferId, password);
}
promise.then( ({ user })=> {
const sessionId = dao.createNewSessionId(user.uid);
const sessionId = authDao.createNewSessionId(user.uid);
res.json({sessionId});
}).catch(error => {
res.json( {error: error.code} );
@ -92,19 +95,26 @@ function signin(req, res) {
* @param {Object} req { tagferId: String, email: String, password: String, phoneNumber: String, fullName: String }
* @param {Object} res { sessionId: String } | { error: String }
*/
function signup(req, res) {
const user = req.body;
const verifier = () => user.tagferId && user.email && user.password && user.phoneNumber && user.fullName;
if (!utils.isAppSecretValid(req,res) || !utils.isBodyValid(verifier, res)) {
async function signup(req, res) {
const {user, profile} = req.body;
const userVerifier = () => user.tagferId && user.email && user.password && user.phoneNumber && user.fullName;
const profileVerifier = () => profile.company && profile.company.profession && profile.company.name && profile.company.address && profile.company.number;
if (!utils.isAppSecretValid(req,res) || !utils.isBodyValid(userVerifier, res) || !utils.isBodyValid(profileVerifier, res)) {
return;
}
dao.createNewUser(user).then( user => {
const sessionId = dao.createNewSessionId(user.uid);
res.json({sessionId});
}).catch(error => {
res.json( {error: error.code} );
});
profile.name = appConfig.defaultProfileName;
profile.email = user.email
try {
await authDao.createNewUser(user);
await profileDao.createInitialProfiles(profile, user.tagferId);
const sessionId = authDao.createNewSessionId(user.tagferId);
res.status(http.CREATED).json({sessionId});
} catch (error) {
res.status(http.BAD_REQUEST).json({error:error.code});
}
}
/**

28
src/ups/profiles/dao.js Normal file
View File

@ -0,0 +1,28 @@
const database = require('firebase-admin').database();
/**
* Blind initializing function for a new user's default profile
* @param {object} profileObj
* @param {string} tagferId
*/
function createInitialProfiles(profileObj, tagferId) {
//persist profile data to firebase
return database.ref(`/profiles/${tagferId}/profile1`).set(profileObj);
}
/**
* Updates a user profile
* @param {object} profileObj JSON object containing all data for a profile captured from the frontend
* @param {number} profileNumber Number used to identify which profile a user wants to update/add to if the profile slot is empty
* @param {string} tagferId tagferId obtained by extracting from authorization header
* @returns {Boolean} Boolean result of whether the
*/
function updateProfile(profileObj, profileNumber, tagferId) {
//persist profile data to firebase
return database.ref(`/profiles/${tagferId}/profile${profileNumber}`).set(profileObj);
}
module.exports = {
updateProfile,
createInitialProfiles
};

View File

@ -0,0 +1,38 @@
const profileDao = require('./dao');
const authDao = require('../auth/dao');
const utils = require('../utils/utils');
const errors = require('../../config/errors');
const http = require('../../config/http');
const _ = require('lodash');
// Handlers
/**
* Endpoints: POST profiles/
* Updates a profile for a user based on session stored tagferId
* @param {Object} req
* @param {Object} res {result: Boolean} | {error: String}
*/
async function updateUserProfile(req, res) {
const profileObj = req.body;
const profileNumber = req.params.profileNumber;
if (!utils.isProfileNumberValid(profileNumber, res)) {
return;
} else {
const sessionId = utils.getSessionIdFromAuthHeader(req, res);
try {
const tagferId = authDao.getSession(sessionId).tagferId;
profileDao.updateProfile(profileObj, profileNumber, tagferId).then( () => {
res.status(http.CREATED).json({})
}).catch( (error) => {
res.status(http.INTERNAL_SERVER_ERROR).json({error: errors.APP_FIREBASE_DATABASE_ERROR})
});
} catch (error) {
res.status(http.UNAUTHORIZED).json({error})
}
}
}
module.exports = {
updateUserProfile
}

View File

@ -1,6 +1,7 @@
const appConfig = require('../../config/app.json');
const http = require('../../config/http');
const errors = require('../../config/errors');
const auth = require('../auth/dao');
/**
* Verifies if the request is valid by checking if the request has the right app secret.
@ -13,12 +14,22 @@ function isAppSecretValid(req, res) {
const sysToken = appConfig.keys.appSecret;
if( usrToken !== sysToken) {
res.status(http.UNAUTHORIZED).json({ error: 'Unauthorized access into api' });
res.status(http.UNAUTHORIZED).json({ error: errors.AUTH_UNAUTHORIZED_ACCESS });
return false;
}
return true;
}
function getSessionIdFromAuthHeader(req, res) {
const usrToken = req.headers.authorization;
if (usrToken) {
return usrToken;
} else {
return null;
}
}
/**
* Checks if the body is valid
* @param {Function} isValid verifier
@ -32,7 +43,18 @@ function isBodyValid(isValid, response) {
return true;
}
function isProfileNumberValid(profileNumber, response) {
if (profileNumber > 4 || profileNumber < 1) {
response.status(http.BAD_REQUEST).json({error: errors.NO_PROFILE_FOUND_FOR_NUMBER});
return false;
} else {
return true;
}
}
module.exports = {
isAppSecretValid,
isBodyValid
isBodyValid,
getSessionIdFromAuthHeader,
isProfileNumberValid
};