Skip to content

Commit

Permalink
Merge e1f6321 into aea5f0e
Browse files Browse the repository at this point in the history
  • Loading branch information
victor-shagor committed Aug 27, 2019
2 parents aea5f0e + e1f6321 commit 0521397
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 15 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"express": "^4.16.3",
"express-jwt": "^5.3.1",
"express-session": "^1.15.6",
"express-validator": "5.3.1",
"jsonwebtoken": "^8.3.0",
"method-override": "^2.3.10",
"methods": "^1.1.2",
Expand Down
4 changes: 2 additions & 2 deletions src/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ const databaseEnvDetails = {

const config = {
development: {
...databaseEnvDetails,
database: process.env.DB_CONFIG_DEV,
...databaseEnvDetails,
},
test: {
database: process.env.DB_CONFIG_TEST,
...databaseEnvDetails
},
production: {
DATABASE_URL: process.env.DATABASE_URL,
DATABASE_URL: process.env.DATABASE_URL
}
};

Expand Down
40 changes: 38 additions & 2 deletions src/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ class UserController {
const tokenCreated = await Authentication.getToken({
id: userFound.id,
username: userFound.username,
role: userFound.roleId,
role: userFound.role,
});
if (tokenCreated) {
const userDetails = {
id: userFound.dataValues.id,
username: userFound.dataValues.username,
role: userFound.dataValues.roleId,
role: userFound.dataValues.role,
token: tokenCreated,
};
return HelperMethods.requestSuccessful(res, {
Expand Down Expand Up @@ -184,6 +184,42 @@ class UserController {
return HelperMethods.serverError(res);
}
}

/**
* Verify a user's email
* Route: POST: /updateuser
* @param {object} req - HTTP Request object
* @param {object} res - HTTP Response object
* @return {res} res - HTTP Response object
* @memberof UserController
*/
static async updatedUser(req, res) {
const payload = req.decoded;
const { role, email } = req.body;
try {
if (payload.role !== 'Super Administrator') {
return HelperMethods.clientError(res, 'Only a super admin'
+ 'can update user role', 401);
}
const userToUpdate = await models.User.findOne({ where: { email } });
if (!userToUpdate) {
return HelperMethods.clientError(res,
'User not found', 404);
}
if (userToUpdate.role === role) {
return HelperMethods.clientError(res, `user is already a ${role}`, 409);
}
await userToUpdate.update({ role });
return HelperMethods
.requestSuccessful(res, {
success: true,
message: 'Role updated successfully',
data: userToUpdate
}, 200);
} catch (error) {
return HelperMethods.serverError(res);
}
}
}

export default UserController;
Expand Down
5 changes: 5 additions & 0 deletions src/migrations/01-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ module.exports = {
profileImage: {
type: Sequelize.STRING,
},
role: {
type: Sequelize.STRING,
allowNull: false,
defaultValue: 'Requester'
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
Expand Down
6 changes: 5 additions & 1 deletion src/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,11 @@ export default (sequelize, DataTypes) => {
args: [6, 150],
msg: 'Password must be more than 5 characters'
}
}
},
},
role: {
type: DataTypes.ENUM(['Super Administrator', 'Travel Administrator',
'Travel Team Member', 'Manager', 'Requester'])
},
profileImage: {
type: DataTypes.STRING,
Expand Down
12 changes: 12 additions & 0 deletions src/routes/User.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { UserController } from '../controllers';
import Authorization from '../middlewares';
import Validate from '../validation';

const userRoutes = app => {
app.patch('/api/v1/updateuser',
Authorization.checkToken,
Validate.validateRoleUpdate,
UserController.updatedUser);
};

export default userRoutes;
1 change: 1 addition & 0 deletions src/routes/authRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ const authRoutes = app => {
};

export default authRoutes;

2 changes: 2 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import authRoute from './authRoute';
import requestRoute from './requestRoute';
import userRoutes from './User';

/**
* Handles request
Expand All @@ -16,6 +17,7 @@ const routes = app => {
});
authRoute(app);
requestRoute(app);
userRoutes(app);
};

export default routes;
11 changes: 9 additions & 2 deletions src/seeders/01-demo-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
email: 'demo1@demo.com',
username: 'user1',
isVerified: true,
role: 'Super Administrator',
password: await init(),
createdAt: new Date(),
updatedAt: new Date()
Expand All @@ -24,7 +25,9 @@ module.exports = {
lastName: 'Doe',
email: 'demo2@demo.com',
username: 'user2',
isVerified: true,
password: await init(),
role: 'Manager',
createdAt: new Date(),
updatedAt: new Date()
},
Expand All @@ -33,8 +36,10 @@ module.exports = {
firstName: 'John',
lastName: 'Doe',
email: 'demo3@demo.com',
username: 'user2',
username: 'user3',
isVerified: true,
password: await init(),
role: 'Manager',
createdAt: new Date(),
updatedAt: new Date()
},
Expand All @@ -43,8 +48,10 @@ module.exports = {
firstName: 'John',
lastName: 'Doe',
email: 'demo4@demo.com',
username: 'user2',
username: 'user4',
password: await init(),
isVerified: true,
role: 'Manager',
createdAt: new Date(),
updatedAt: new Date()
}], {}),
Expand Down
99 changes: 97 additions & 2 deletions src/test/integrationTests/userController.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import app from '../../index';
import { UserController } from '../../controllers';

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

let token;
let token1;
describe('Integration tests for the user controller', () => {
describe('Test general error handling and welcome message', () => {
it('should send an error when there is an unforeseen error', async () => {
Expand Down Expand Up @@ -108,6 +110,18 @@ describe('Integration tests for the user controller', () => {
it('should log a user in when valid details are given', async () => {
const response = await chai.request(app).post('/api/v1/auth/login')
.send({ email: 'demo1@demo.com', password: 'password' });
token = response.body.data.userDetails.token;
expect(response.status).to.deep.equal(200);
expect(response.body.data).to.have.property('message');
expect(response.body.data.message).to.equal('Login successful');
expect(response.body.data).to.have.property('success');
expect(response.body.data.success).to.equal(true);
expect(response.body.data.userDetails).to.be.an('object');
});
it('should log a user in when valid details are given', async () => {
const response = await chai.request(app).post('/api/v1/auth/login')
.send({ email: 'demo2@demo.com', password: 'password' });
token1 = response.body.data.userDetails.token;
expect(response.status).to.deep.equal(200);
expect(response.body.data).to.have.property('message');
expect(response.body.data.message).to.equal('Login successful');
Expand All @@ -128,7 +142,6 @@ describe('Integration tests for the user controller', () => {
expect(response.body.message)
.to.equal('Invalid request. All fields are required');
});

it('should return client error when user details is missing', async () => {
const userDetails = {
password: 'johndoe@wemail.com',
Expand All @@ -143,4 +156,86 @@ describe('Integration tests for the user controller', () => {
.to.equal('Invalid request. All fields are required');
});
});
describe('users', () => {
it('Should not update user without email', done => {
chai.request(app)
.patch('/api/v1/updateuser')
.send({
email: '', role: 'Travel Administrator',
})
.set({
'x-access-token': token
})
.end((err, res) => {
res.should.have.status(400);
res.body.should.be.a('object');
res.body.should.have.property('message');
done();
});
});
it('should not update a wrong email', done => {
chai.request(app)
.patch('/api/v1/updateuser')
.send({
email: 'uryyeh@gmail.com', role: 'Travel Administrator',
})
.set({
'x-access-token': token
})
.end((err, res) => {
res.should.have.status(404);
res.body.should.be.a('object');
res.body.should.have.property('message');
done();
});
});
it('should update user role', done => {
chai.request(app)
.patch('/api/v1/updateuser')
.send({
email: 'demo3@demo.com', role: 'Travel Administrator',
})
.set({
'x-access-token': token
})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a('object');
res.body.should.have.property('data');
done();
});
});
it('should not update user role with thesame role', done => {
chai.request(app)
.patch('/api/v1/updateuser')
.send({
email: 'demo3@demo.com', role: 'Travel Administrator',
})
.set({
'x-access-token': token
})
.end((err, res) => {
res.should.have.status(409);
res.body.should.be.a('object');
res.body.should.have.property('message');
done();
});
});
it('should not update user without super admin token', done => {
chai.request(app)
.patch('/api/v1/updateuser')
.send({
email: 'demo3@demo.com', role: 'Travel Administrator',
})
.set({
'x-access-token': token1
})
.end((err, res) => {
res.should.have.status(401);
res.body.should.be.a('object');
res.body.should.have.property('message');
done();
});
});
});
});
1 change: 0 additions & 1 deletion src/utils/cryptData.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

import bcrypt from 'bcrypt';

/**
Expand Down
15 changes: 15 additions & 0 deletions src/validation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@ class Validate {
if (!email) return allFieldsRequired(res);
next();
}

/**
* @param {object} req - Request object
* @param {object} res - Response object
* @param {callback} next - The callback that passes the request to the next handler
* @returns {object} res - Response object when query is invalid
* @memberof Validate
*/
static validateRoleUpdate(req, res, next) {
req.body = trimValues(req.body);
const { email, role } = req.body;
if (!email) return allFieldsRequired(res);
if (!role) return allFieldsRequired(res);
next();
}
}

export default Validate;
Expand Down
18 changes: 13 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2167,6 +2167,14 @@ express-unless@^0.3.0:
resolved "https://registry.yarnpkg.com/express-unless/-/express-unless-0.3.1.tgz#2557c146e75beb903e2d247f9b5ba01452696e20"
integrity sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=

express-validator@5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-5.3.1.tgz#6f42c6d52554441b0360c40ccfb555b1770affe2"
integrity sha512-g8xkipBF6VxHbO1+ksC7nxUU7+pWif0+OZXjZTybKJ/V0aTVhuCoHbyhIPgSYVldwQLocGExPtB2pE0DqK4jsw==
dependencies:
lodash "^4.17.10"
validator "^10.4.0"

express@^4.16.3:
version "4.17.1"
resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134"
Expand Down Expand Up @@ -3411,7 +3419,7 @@ lodash.set@^4.0.0:
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=

lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5:
lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
Expand Down Expand Up @@ -5002,9 +5010,9 @@ sequelize-pool@^2.3.0:
integrity sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==

sequelize@^5.14.0:
version "5.15.1"
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.15.1.tgz#f130ded17e74395ae7f5e265277c99577e895afb"
integrity sha512-DCzzJYvJLMKnyf8G3at2A+yM9M2fSQmTmuOYIpCWM8Gjqx3XfgNTd1NkuyPWFoi1/d1AXQsN2VDPXkPczida8A==
version "5.16.0"
resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-5.16.0.tgz#6626f82cc41cdf02a5758f73783566e369d05cd8"
integrity sha512-1b2PNgxV3+HtaOCJ9gos4qQzvd8QEc5jz77MUxZg8+nqX4haSZWI7Rn0XzW1lPIRseKUXVHanyh/gqztsfJN+g==
dependencies:
bluebird "^3.5.0"
cls-bluebird "^2.1.0"
Expand Down Expand Up @@ -5749,7 +5757,7 @@ validate-npm-package-license@^3.0.1:
spdx-correct "^3.0.0"
spdx-expression-parse "^3.0.0"

validator@^10.11.0:
validator@^10.11.0, validator@^10.4.0:
version "10.11.0"
resolved "https://registry.yarnpkg.com/validator/-/validator-10.11.0.tgz#003108ea6e9a9874d31ccc9e5006856ccd76b228"
integrity sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==
Expand Down

0 comments on commit 0521397

Please sign in to comment.