-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #43 from akhilome/ft-user-signup-160819724
#160819724 Customer can sign up for an account
- Loading branch information
Showing
12 changed files
with
289 additions
and
6 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,12 @@ | ||
{ | ||
"presets": [ | ||
"env", | ||
"stage-2" | ||
"stage-2", | ||
[ | ||
"env", { | ||
"targets": { | ||
"node": "8.12.0" | ||
} | ||
} | ||
] | ||
] | ||
} |
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
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import bcrpyt from 'bcryptjs'; | ||
import pool from '../db/config'; | ||
|
||
class AuthController { | ||
static async signup(req, res) { | ||
const { name, email, password } = req; | ||
const isAdmin = email === 'hovkard@gmail.com' ? 't' : 'f'; | ||
|
||
try { | ||
// Check if a user with the provided email already exists | ||
const existingUser = (await pool.query('SELECT * FROM users WHERE email=$1', [email])).rowCount; | ||
if (existingUser) { | ||
return res.status(400).json({ | ||
status: 'error', | ||
message: 'a user with that email already exists', | ||
}); | ||
} | ||
// Hash password and save user to database | ||
const hashedPassword = await bcrpyt.hash(password, 10); | ||
const dbQuery = 'INSERT INTO users(name, email, password, is_admin) VALUES($1, $2, $3, $4) RETURNING id, name, email'; | ||
const user = (await pool.query(dbQuery, [name, email, hashedPassword, isAdmin])).rows[0]; | ||
return res.status(201).json({ | ||
status: 'success', | ||
message: 'user created successfully', | ||
user, | ||
}); | ||
} catch (error) { | ||
return res.status(400).json({ error }); | ||
} | ||
} | ||
} | ||
|
||
export default AuthController; |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import Validator from '../validators/validator'; | ||
|
||
class Sanitize { | ||
static signup(req, res, next) { | ||
const { | ||
name, | ||
email, | ||
password, | ||
confirmPassword, | ||
} = req.body; | ||
|
||
const missingFields = [name, email, password, confirmPassword].map((field, index) => { | ||
const keys = { | ||
0: 'name', | ||
1: 'email', | ||
2: 'password', | ||
3: 'confirm password', | ||
}; | ||
return field === undefined ? keys[index] : null; | ||
}).filter(field => field !== null).join(', '); | ||
|
||
if (!name || !email || !password || !confirmPassword) { | ||
return res.status(400).json({ | ||
status: 'error', | ||
message: `you're missing these fields: ${missingFields}`, | ||
}); | ||
} | ||
|
||
const response = message => res.status(400).json({ status: 'error', message }); | ||
|
||
if (!Validator.isValidName(name)) return response('invalid name'); | ||
if (!Validator.isEmail(email)) return response('invalid email'); | ||
if (!Validator.isMatchingPasswords(password, confirmPassword)) return response('provided passwords donot match'); | ||
if (!Validator.isValidPassword(password)) return response('invalid password'); | ||
|
||
req.name = name.trim(); | ||
req.email = email.trim(); | ||
req.password = password.trim(); | ||
return next(); | ||
} | ||
} | ||
|
||
export default Sanitize; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { Router } from 'express'; | ||
import AuthController from '../controllers/authController'; | ||
import Sanitize from '../middleware/sanitizer'; | ||
|
||
const router = new Router(); | ||
router.post('/signup', Sanitize.signup, AuthController.signup); | ||
|
||
export default router; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
class Validator { | ||
static isEmail(email) { | ||
const re = /[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}/ig; | ||
return re.test(email.trim().toLowerCase()); | ||
} | ||
|
||
static isValidPassword(password) { | ||
return password.trim().length > 5; | ||
} | ||
|
||
static isMatchingPasswords(password, confirmPassword) { | ||
return password.trim() === confirmPassword.trim(); | ||
} | ||
|
||
static isValidName(name) { | ||
return name.trim().length >= 2; | ||
} | ||
} | ||
|
||
/* Refs. | ||
email regex pattern credit: https://www.regular-expressions.info/email.html?wlr=1 | ||
*/ | ||
|
||
export default Validator; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import chai from 'chai'; | ||
import 'chai/register-should'; | ||
import chaiHttp from 'chai-http'; | ||
import dirtyChai from 'dirty-chai'; | ||
import app from '../../server/index'; | ||
import { seedData, populateTables } from '../seed/seed'; | ||
|
||
chai.use(chaiHttp); | ||
chai.use(dirtyChai); | ||
|
||
describe('POST /auth/signup', () => { | ||
before(populateTables); | ||
|
||
it('should signup a valid user successfully', (done) => { | ||
chai.request(app) | ||
.post('/api/v1/auth/signup') | ||
.send(seedData.users.admin) | ||
.end((err, res) => { | ||
if (err) done(err); | ||
|
||
res.status.should.eql(201); | ||
res.body.should.be.an('object').that.has.keys(['status', 'message', 'user']); | ||
res.body.status.should.eql('success'); | ||
res.body.user.should.have.keys(['id', 'name', 'email']); | ||
res.body.user.name.should.eql(seedData.users.admin.name); | ||
res.body.user.email.should.eql(seedData.users.admin.email); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should not signup a user with no name', (done) => { | ||
chai.request(app) | ||
.post('/api/v1/auth/signup') | ||
.send(seedData.users.invalidUserNoName) | ||
.end((err, res) => { | ||
if (err) done(err); | ||
|
||
res.status.should.eql(400); | ||
res.body.should.have.keys(['status', 'message']); | ||
res.body.should.not.have.keys(['user']); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should not signup a user with no email', (done) => { | ||
chai.request(app) | ||
.post('/api/v1/auth/signup') | ||
.send(seedData.users.invalidUserNoEmail) | ||
.end((err, res) => { | ||
if (err) done(err); | ||
|
||
res.status.should.eql(400); | ||
res.body.should.have.keys(['status', 'message']); | ||
res.body.should.not.have.keys(['user']); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should not signup a user with no password', (done) => { | ||
chai.request(app) | ||
.post('/api/v1/auth/signup') | ||
.send(seedData.users.invalidUserNoPass) | ||
.end((err, res) => { | ||
if (err) done(err); | ||
|
||
res.status.should.eql(400); | ||
res.body.should.have.keys(['status', 'message']); | ||
res.body.should.not.have.keys(['user']); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should not signup a user with missmatching passwords', (done) => { | ||
chai.request(app) | ||
.post('/api/v1/auth/signup') | ||
.send(seedData.users.invalidUserPassMissMatch) | ||
.end((err, res) => { | ||
if (err) done(err); | ||
res.status.should.eql(400); | ||
|
||
res.body.should.have.keys(['status', 'message']); | ||
res.body.should.not.have.keys(['user']); | ||
res.body.message.should.eql('provided passwords donot match'); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should not signup a user if email already exists', (done) => { | ||
chai.request(app) | ||
.post('/api/v1/auth/signup') | ||
.send(seedData.users.admin) | ||
.end((err, res) => { | ||
if (err) done(err); | ||
res.status.should.eql(400); | ||
res.body.status.should.eql('error'); | ||
res.body.message.should.eql('a user with that email already exists'); | ||
done(); | ||
}); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import pool from '../../server/db/config'; | ||
|
||
const seedData = { | ||
users: { | ||
admin: { | ||
name: 'Kizito', | ||
email: 'hovkard@gmail.com', | ||
password: 'suppersecurepassword', | ||
confirmPassword: 'suppersecurepassword', | ||
}, | ||
validUser: { | ||
name: 'James', | ||
email: 'daniel@james.com', | ||
password: 'pixel2user', | ||
confirmPassword: 'pixel2user', | ||
}, | ||
invalidUserNoData: {}, | ||
invalidUserNoName: { | ||
email: 'unserious@lad.com', | ||
password: 'insecure', | ||
confirmPassword: 'insecure', | ||
}, | ||
invalidUserNoEmail: { | ||
name: 'Name?', | ||
password: 'pass', | ||
confirmPassword: 'pass', | ||
}, | ||
invalidUserNoPass: { | ||
name: 'Magician', | ||
email: 'an@email.address', | ||
}, | ||
invalidUserPassMissMatch: { | ||
name: 'Olodo', | ||
email: 'another@sweet.email', | ||
password: 'oneThing', | ||
confirmPassword: 'anEntirelyDifferentThing', | ||
}, | ||
}, | ||
}; | ||
|
||
const populateTables = async () => { | ||
const dropUsersTableQuery = 'DROP TABLE IF EXISTS users'; | ||
|
||
const createUsersTableQuery = `CREATE TABLE users ( | ||
id serial PRIMARY KEY, | ||
name VARCHAR(50) NOT NULL, | ||
email VARCHAR(50) UNIQUE NOT NULL, | ||
password VARCHAR(255) NOT NULL, | ||
is_admin BOOLEAN NOT NULL | ||
)`; | ||
|
||
await pool.query(dropUsersTableQuery); | ||
await pool.query(createUsersTableQuery); | ||
}; | ||
|
||
export { seedData, populateTables }; |