Skip to content

Commit

Permalink
Merge 0764aa6 into 5781f0d
Browse files Browse the repository at this point in the history
  • Loading branch information
madeofhuman committed Sep 27, 2018
2 parents 5781f0d + 0764aa6 commit 7ff5996
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 85 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"main": "server/index.js",
"scripts": {
"test": "npm run test:v1",
"test:v1": "npm run migrate:test && cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha --timeout 30000 --exit --require babel-register ./server/v1/tests/**/*.test.js",
"test:v1": "npm run migrate:test && cross-env NODE_ENV=test nyc --reporter=html --reporter=text mocha --timeout 90000 --exit --require babel-register ./server/v1/tests/**/*.test.js",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"coveralls": "nyc --reporter=lcov --reporter=text-lcov npm test",
"prestart": "sequelize db:migrate",
Expand Down
4 changes: 2 additions & 2 deletions server/config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ const strategies = {
google: {
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL || 'http://elven-ah-client.herokuapp.com/oauth2/google',
callbackURL: process.env.GOOGLE_CALLBACK_URL || 'https://elven-ah-develop.herokuapp.com/oauth2/google',
},
facebook: {
clientID: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
callbackURL: process.env.FACEBOOK_CALLBACK_URL || 'http://elven-ah-client.herokuapp.com/oauth2/facebook',
callbackURL: process.env.FACEBOOK_CALLBACK_URL || 'https://elven-ah-develop.herokuapp.com/oauth2/facebook',
},
twitter: {
consumerKey: 'get_your_own',
Expand Down
6 changes: 3 additions & 3 deletions server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ app.all('*', (req, res) => {

// Error handler
// no stack traces leaked to user in production
app.use((err, req, res) => {
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
res.send({
status: 'error',
message: err.message,
error: env === 'production' ? {} : err,
error: env === 'production' ? {} : next(err),
});
});
// finally, let's start our server...
Expand Down
123 changes: 113 additions & 10 deletions server/seeders/20180808181558-demo-Article.js

Large diffs are not rendered by default.

21 changes: 13 additions & 8 deletions server/v1/controllers/AuthController.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import bcrypt from 'bcrypt';
import models from '../../models';
import JwtHelper from '../helpers/JwtHelper';
import NotificationController from './NotificationController';
import randomString from '../helpers/randomString';

const { User } = models;

Expand All @@ -24,6 +25,15 @@ export default class AuthController {
};
}

/**
* @description creates a username from the firstName and a random set of strings
* @param {String} firstName The first name of the user from which the username will be generated
* @returns {String} Returns a username
*/
static createUsername(firstName) {
return `${firstName}-${randomString(4)}`;
}

/**
* @description Formats the message sent to the user on successful login/registration
* This will ensure that all response is formatted in the same way.
Expand Down Expand Up @@ -76,16 +86,11 @@ export default class AuthController {
*/
static signUpUser(req, res, next) {
const {
email, username, firstName, lastName, password,
email, firstName, lastName, password,
} = req.body;
const username = AuthController.createUsername(firstName);
return User.findOrCreate({
where: {
$or: [{
email
}, {
username
}]
},
where: { email },
defaults: {
email,
username,
Expand Down
34 changes: 26 additions & 8 deletions server/v1/controllers/ProfileController.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class ProfileController {
* @param {object} res the response object
* @returns {user} the user object
*/
static getUserProfile(req, res, next) {
static getUserProfile(req, res) {
const { username: loggedInUser } = req.user;
const { username } = req.params;
return User.findOne(Object.assign({}, queryHelper.userProfile, { where: { username } }))
Expand All @@ -28,7 +28,10 @@ export default class ProfileController {
user,
});
}
return next();
res.status(404).json({
status: 'error',
message: 'User with the supplied username does not exist.'
});
});
}

Expand All @@ -38,16 +41,22 @@ export default class ProfileController {
* @param {object} res the response object
* @returns the updated user profile
*/
static updateUserProfile(req, res) {
static async updateUserProfile(req, res) {
const {
email, firstName, lastName, bio, image
email, firstName, lastName, bio, image, username
} = req.body;
const { username: loggedInUser } = req.user;
const { username } = req.params;
if (loggedInUser === username) {
const { username: userToUpdate } = req.params;
if (await ProfileController.usernameExists(username) !== null && username !== loggedInUser) {
return res.status(409).json({
status: 'error',
message: 'The username already exists',
});
}
if (loggedInUser === userToUpdate) {
return User.update({
email, firstName, lastName, bio, image
}, { returning: true, where: { username } })
email, firstName, lastName, bio, image, username,
}, { returning: true, where: { username: loggedInUser } })
.then(([, [user]]) => res.status(200).json({
status: 'success',
user: AuthController.stripeUser(user),
Expand All @@ -59,6 +68,15 @@ export default class ProfileController {
});
}

/**
* @description checks if new username exists in the db
* @param {string} username the new username
*/
static async usernameExists(username) {
const existingUser = await User.findOne({ where: { username: `${username}` } });
return existingUser;
}

/**
* @description Prevents email duplicates by checking if the email already exists.
* @param {object} req the request object
Expand Down
1 change: 1 addition & 0 deletions server/v1/controllers/SearchController.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class SearchController {
const keyword = req.query.q;
const userSearch = await User.findAll({
where: { $or: { username: { $ilike: `%${keyword}%` }, firstName: { $ilike: `%${keyword}%` }, lastName: { $ilike: `%${keyword}%` }, }, },
attributes: { exclude: ['password'] },
order: [
['username', 'DESC']
],
Expand Down
2 changes: 1 addition & 1 deletion server/v1/middlewares/validations/UserValidation.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ class UserValidation {
firstName: 'required|alpha|min:2|max:100',
lastName: 'required|alpha|min:2|max:100',
email: 'required|email',
username: 'required|alpha_num|min:5|max:15',
password: 'required|alpha_num|min:8|max:20',
confirmPassword: 'required|alpha_num|min:8|max:20|same:password',
};
Expand Down Expand Up @@ -92,6 +91,7 @@ class UserValidation {
const userUpdateProperties = {
firstName: 'alpha|min:2|max:100',
lastName: 'alpha|min:2|max:100',
username: 'required|alpha_num|min:5|max:105',
email: 'required|email',
bio: 'min:20|max:4000',
image: 'url'
Expand Down
33 changes: 31 additions & 2 deletions server/v1/tests/userProfile.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ describe('GET /api/v1/users/:username Tests for user view profile endpoint', ()
done();
});
});

it('should return 404 when a non-existent username is supplied', (done) => {
chai.request(app).get('/api/v1/users/jjahhshg')
.set('x-access-token', userToken)
.end((req, res) => {
res.status.should.eql(404);
res.body.should.be.an('object');
res.body.should.have.property('message').include('User with the supplied username does not exist.');
done();
});
});
});

describe('PUT /api/v1/users/:username Tests for user update profile endpoint', () => {
Expand All @@ -80,6 +91,7 @@ describe('PUT /api/v1/users/:username Tests for user update profile endpoint', (
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@mail.com',
username: 'JohnAwesome',
bio: `John Doe was born in 1977 when he arrived in Los Angeles.
His previous life in Tennessee,
Wisconsin & Baltimore was a great & fertile time but
Expand Down Expand Up @@ -109,6 +121,7 @@ describe('PUT /api/v1/users/:username Tests for user update profile endpoint', (
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@mail.com',
username: 'JohnAwesome',
bio: `John Doe was born in 1977 when he arrived in Los Angeles.
His previous life in Tennessee,
Wisconsin & Baltimore was a great & fertile time but
Expand All @@ -124,7 +137,7 @@ describe('PUT /api/v1/users/:username Tests for user update profile endpoint', (
});
});

it('should return 400 when a user does not provide an email', (done) => {
it('should return 400 when a user does not provide an email or username', (done) => {
chai.request(app).put('/api/v1/users/JohnAwesome')
.set('x-access-token', userToken)
.end((req, res) => {
Expand All @@ -134,6 +147,7 @@ describe('PUT /api/v1/users/:username Tests for user update profile endpoint', (
res.body.should.have.property('errors');
res.body.errors.should.be.a('object');
res.body.errors.should.have.property('email').include('The email field is required.');
res.body.errors.should.have.property('username').include('The username field is required.');
done();
});
});
Expand All @@ -160,7 +174,22 @@ describe('PUT /api/v1/users/:username Tests for user update profile endpoint', (
chai.request(app).put('/api/v1/users/JohnAwesome')
.set('x-access-token', userToken)
.send({
email: 'janeBlaise@gmail.com',
email: 'johndoe@mail.com',
username: 'oyomi'
})
.end((req, res) => {
res.status.should.eql(409);
res.body.should.be.a('object');
res.body.should.have.property('status').eql('error');
done();
});
});
it('should return 409 when a user wants to update to a username that belongs to another user', (done) => {
chai.request(app).put('/api/v1/users/JohnAwesome')
.set('x-access-token', userToken)
.send({
email: 'johndoe@mail.com',
username: 'oyomi'
})
.end((req, res) => {
res.status.should.eql(409);
Expand Down
56 changes: 6 additions & 50 deletions server/v1/tests/userSignup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ import chai from 'chai';
import chaiHttp from 'chai-http';
import app from '../..';
import JwtHelper from '../helpers/JwtHelper';
import AuthController, {} from '../controllers/AuthController';

chai.should();
chai.use(chaiHttp);

const user = {
firstName: 'John',
lastName: 'Doe',
username: 'johnny',
email: 'testuser@test.com',
password: 'Qwertyui0p',
confirmPassword: 'Qwertyui0p',
};
const userWithAlreadyUsedEmail = {
firstName: 'John',
lastName: 'Doe',
username: 'johnnyllllee',
email: 'testuser@test.com',
password: 'Qwertyui0p',
confirmPassword: 'Qwertyui0p',
Expand All @@ -46,7 +45,7 @@ describe('User signup', () => {
});

it('should not re-send the verification email when requested by the user if no email is sent', (done) => {
chai.request(app).post('/api/v1/auth/verify').send({ username: user.username }).end((err, res) => {
chai.request(app).post('/api/v1/auth/verify').send({ }).end((err, res) => {
res.status.should.eql(400);
res.body.should.be.an('object').with.property('errors');
done();
Expand Down Expand Up @@ -89,7 +88,6 @@ describe('User signup', () => {
it('should return 400 when a user does not provide a firstName', (done) => {
chai.request(app).post('/api/v1/auth/signup').send({
lastName: 'Yomi',
username: 'oyomi',
email: 'johndoe@gmail.com',
password: '8pcutTway',
confirmPassword: '8pcutTway',
Expand All @@ -107,7 +105,6 @@ describe('User signup', () => {
it('should return 400 when a user does not provide a lastName', (done) => {
chai.request(app).post('/api/v1/auth/signup').send({
firstName: 'Doe',
username: 'Johnny',
email: 'johndoe@gmail.com',
password: 'Opcut2way',
confirmPassword: 'Opcut2way',
Expand All @@ -126,7 +123,6 @@ describe('User signup', () => {
chai.request(app).post('/api/v1/auth/signup').send({
firstName: 'John',
lastName: 'Doe',
username: 'Johnny',
password: 'Opcut2way',
confirmPassword: 'Opcut2way',
}).end((req, res) => {
Expand All @@ -140,30 +136,11 @@ describe('User signup', () => {
});
});

it('should return 400 when a user does not provide a username', (done) => {
chai.request(app).post('/api/v1/auth/signup').send({
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
password: 'Opcut2way',
confirmPassword: 'Opcut2way',
}).end((req, res) => {
res.status.should.eql(400);
res.body.should.be.a('object');
res.body.should.have.property('status').eql('error');
res.body.should.have.property('errors');
res.body.errors.should.be.a('object');
res.body.errors.should.have.property('username').include('The username field is required.');
done();
});
});

it('should return 400 when a user does not provide a password', (done) => {
chai.request(app).post('/api/v1/auth/signup').send({
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
username: 'Johnny',
confirmPassword: 'Opcut2way',
}).end((req, res) => {
res.status.should.eql(400);
Expand All @@ -181,7 +158,6 @@ describe('User signup', () => {
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
username: 'Johnny',
password: 'Opcut',
}).end((req, res) => {
res.status.should.eql(400);
Expand All @@ -199,7 +175,6 @@ describe('User signup', () => {
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
username: 'Johnny',
password: 'Opcutuyh',
confirmPassword: 'Opcutuyh',
}).end((req, res) => {
Expand All @@ -218,7 +193,6 @@ describe('User signup', () => {
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
username: 'Johhny',
password: 'Opcut2way',
}).end((req, res) => {
res.status.should.eql(400);
Expand All @@ -236,7 +210,6 @@ describe('User signup', () => {
firstName: 'John',
lastName: 'Doe',
email: 'johndoe@gmail.com',
username: 'Johhny',
password: 'Opcut2way',
confirmPassword: 'Opcut2wau',
}).end((req, res) => {
Expand All @@ -262,26 +235,9 @@ describe('User signup', () => {
});
});

it('should return 409 when a user with the username already exists', (done) => {
chai.request(app).post('/api/v1/auth/signup').send(user).end((req, res) => {
res.status.should.eql(409);
res.body.should.be.a('object');
res.body.should.have.property('status').eql('error');
res.body.should.have.property('errors');
res.body.errors.should.be.a('object');
res.body.errors.should.have.property('username').include('User with username: johnny already exists.');
done();
});
});

it('should return 409 when a user with the username and email already exists', (done) => {
chai.request(app).post('/api/v1/auth/signup').send(user).end((req, res) => {
res.status.should.eql(409);
res.body.should.be.a('object');
res.body.should.have.property('status').eql('error');
res.body.errors.should.have.property('username').include('User with username: johnny already exists.');
res.body.errors.should.have.property('email').include('User with email: testuser@test.com already exists.');
done();
});
it('should automatically create a username for the user from their first name', () => {
const username = AuthController.createUsername(user.firstName);
username.should.be.a('string');
username.should.include('-');
});
});

0 comments on commit 7ff5996

Please sign in to comment.