Skip to content

Commit

Permalink
Merge pull request #12 from andela/ft-user-signout-backend-166841010
Browse files Browse the repository at this point in the history
#166841010 User should be able to sign out
  • Loading branch information
fob413 committed Jul 5, 2019
2 parents 6cd0a7d + 9fcad37 commit 6798511
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 27 deletions.
71 changes: 71 additions & 0 deletions src/controllers/Auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import auth from '../middleware/Auth';
import { Blacklist, User } from '../db/models';
/**
*
*
* @class AuthController
*/
class AuthController {
/**
*
*
* @static
* @param {object} req
* @param {object} res
* @returns {object} res
* @memberof AuthController
*/
static async login(req, res) {
const { email } = req.body;
try {
const user = await User.findOne({
where: { email },
attributes: { exclude: ['password'] }
});
const { id } = user;
const userToken = auth.authenticate(id);
if (user) {
return res.status(200).json({
status: 200,
message: 'User successfully Logged In',
data: userToken
});
}
} catch (error) {
return res.status(500).json({
status: 500,
message: error,
});
}
}

/**
*
*@description Logout a user
* @static
* @param {object} req
* @param {object} res
* @returns {object} res
* @memberof AuthController
*/
static async logOut(req, res) {
const { token } = req.headers || req.body || req.query;
try {
const createdToken = await Blacklist.create({
token
});
return res.status(200).json({
status: 200,
message: 'User successfully Logged Out',
data: createdToken
});
} catch (error) {
return res.status(500).json({
status: 500,
data: error,
});
}
}
}

export default AuthController;
Empty file removed src/controllers/userController.js
Empty file.
2 changes: 2 additions & 0 deletions src/db/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ module.exports = {
},
isProduction: process.env.NODE_ENV === 'production',
port: process.env.PORT || 3000,
secret: process.env.SECRET,

};
27 changes: 13 additions & 14 deletions src/db/migrations/20190701154222-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,44 +8,43 @@ module.exports = {
},
firstName: {
type: Sequelize.STRING,
required: true,
allowNull: false
required: false,
},
lastName: {
type: Sequelize.STRING,
required: false,
},
userName: {
type: Sequelize.STRING,
required: true,
allowNull: false
unique: true
},
email: {
type: Sequelize.STRING,
required: true,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
required: true,
allowNull: false
},
isVerified: {
type: Sequelize.BOOLEAN,
required: true,
allowNull: false
required: false,
defaultValue: false

},
imageUrl: {
type: Sequelize.STRING,
required: true,
allowNull: false
required: false,
},
bio: {
type: Sequelize.STRING,
required: true,
allowNull: false
required: false,
},
token: {
verificationToken: {
type: Sequelize.STRING,
required: true,
allowNull: false
required: false,
},
createdAt: {
allowNull: false,
Expand Down
28 changes: 28 additions & 0 deletions src/db/migrations/20190701212849-create-blacklist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Blacklists', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
token: {
type: Sequelize.TEXT
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
defaultValue: Sequelize.literal('CURRENT_TIMESTAMP')
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Blacklists');
}
};
11 changes: 11 additions & 0 deletions src/db/models/blacklist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = (sequelize, DataTypes) => {
const Blacklist = sequelize.define('Blacklist', {
token: {
type: DataTypes.TEXT,
},
}, {});
Blacklist.associate = function(models) {
// associations can be defined here
};
return Blacklist;
};
2 changes: 0 additions & 2 deletions src/db/models/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
'use strict';

const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
Expand Down
16 changes: 11 additions & 5 deletions src/db/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: {
type: DataTypes.STRING,
required: true
required: false
},
lastName: {
type: DataTypes.STRING,
required: true
required: false
},
userName: {
type: DataTypes.STRING,
required: true,
unique: true
},
email: {
type: DataTypes.STRING,
Expand All @@ -25,10 +30,11 @@ module.exports = (sequelize, DataTypes) => {
},
imageUrl: {
type: DataTypes.STRING,
required: true
required: false
},
token: {
type: DataTypes.STRING
verificationToken: {
type: DataTypes.STRING,
required: false,
}
}, {});
User.associate = (models) => {
Expand Down
3 changes: 2 additions & 1 deletion src/db/seeders/20190701155734-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ module.exports = {
up: queryInterface => queryInterface.bulkInsert('Users', [{
firstName: 'John',
lastName: 'Doe',
userName: 'JohnDoe',
email: 'john.doe@andela.com',
bio: 'local man is stuck in traffic',
isVerified: true,
password: 'password',
token: '',
verificationToken: '',
imageUrl: 'image.png'
},
], {}),
Expand Down
27 changes: 27 additions & 0 deletions src/middleware/Auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// third-party libraries
import jwt from 'jsonwebtoken';
import config from '../db/config/config';

const { secret } = config;

const auth = {
/**
* @static
* @param {object} user
* @description Generates token for user
* @return {string} string
*/
authenticate(user) {
return jwt.sign(
{
id: user.id,
},
secret,
{
expiresIn: '24h'
}
);
}
};

export default auth;
9 changes: 9 additions & 0 deletions src/routes/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import express from 'express';
import AuthController from '../controllers/Auth';

const router = express.Router();

router.post('/login', AuthController.login);
router.post('/logout', AuthController.logOut);

export default router;
8 changes: 8 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import express from 'express';
import authRoute from './auth';

const router = express.Router();

router.use('/api/v1/auth', authRoute);

export default router;
Empty file removed src/routes/userRoute.js
Empty file.
10 changes: 5 additions & 5 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import errorhandler from 'errorhandler';
import swaggerJSDoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
import config from './db/config/config';
import router from './routes/index';

const { isProduction, port } = config;

Expand Down Expand Up @@ -66,9 +67,10 @@ app.get('/', (req, res) => {
message: 'Welcome to Author\'s Haven',
});
});
app.use(router);

// catch 404 and forward to error handler
app.use((req, res, next) => {
app.use('*', (req, res, next) => {
const err = new Error('Not Found');
err.status = 404;
next(err);
Expand All @@ -79,9 +81,7 @@ app.use((req, res, next) => {
// development error handler
// will print stacktrace
if (!isProduction) {
app.use((err, req, res) => {
console.log(err.stack);

app.use((err, req, res, next) => {
res.status(err.status || 500);

res.json({
Expand All @@ -95,7 +95,7 @@ if (!isProduction) {

// production error handler
// no stacktraces leaked to user
app.use((err, req, res) => {
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
errors: {
Expand Down
36 changes: 36 additions & 0 deletions test/auth.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import app from '../src/server';

chai.should();
chai.use(chaiHttp);
const { expect } = chai;

describe('User Controller', () => {
it('should logout user successfully', (done) => {
chai.request(app).post('/api/v1/auth/logout')
.end((err, res) => {
res.should.have.status(200);
expect(res.body.message).equal('User successfully Logged Out');
expect(res.body.data).to.have.property('id');
expect(res.body.data).to.have.property('token');
done();
});
});
it('should login user successfully', (done) => {
const user = {
email: 'john.doe@andela.com',
password: 'password',
};
chai.request(app).post('/api/v1/auth/login')
.send(user)
.end((err, res) => {
res.should.have.status(200);
expect(res.body.message).equal('User successfully Logged In');
expect(res.body).to.have.property('message');
expect(res.body).to.have.property('data');
expect(res.body).to.have.property('status');
done();
});
});
});
7 changes: 7 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@ describe('server', () => {
done();
});
});
it('Route not found', (done) => {
chai.request(app).get('/error')
.end((err, res) => {
res.should.have.status(404);
done();
});
});
});

0 comments on commit 6798511

Please sign in to comment.