Skip to content

Commit

Permalink
feature(user profile settings): view and edit user profile
Browse files Browse the repository at this point in the history
 - add view user profile endpoint
 - add edit user profile endpoint
 - add mocha test for both view and edit user profile endpoints
 - Solve Hound/Eslint erros

[Starts #169817545]
  • Loading branch information
higustave12 committed Dec 31, 2019
1 parent 4a201ea commit 8b2ffc4
Show file tree
Hide file tree
Showing 11 changed files with 348 additions and 22 deletions.
58 changes: 41 additions & 17 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{

"root": true,
"extends": "airbnb-base",
"env": {
Expand All @@ -9,27 +8,52 @@
},
"rules": {
"one-var": 0,
"arrow-parens": "off",
"object-curly-newline": "off",
"one-var-declaration-per-line": 0,
"new-cap": 0,
"consistent-return": 0,
"no-param-reassign": 0,
"comma-dangle": 0,
"curly": ["error", "multi-line"],
"import/no-unresolved": [2, { "commonjs": true }],
"no-shadow": ["error", { "allow": ["req", "res", "err"] }],
"valid-jsdoc": ["error", {
"requireReturn": true,
"requireReturnType": true,
"requireParamDescription": false,
"requireReturnDescription": true
}],
"require-jsdoc": ["error", {
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true
"curly": [
"error",
"multi-line"
],
"import/no-unresolved": [
2,
{
"commonjs": true
}
}]
],
"no-shadow": [
"error",
{
"allow": [
"req",
"res",
"err"
]
}
],
"valid-jsdoc": [
"error",
{
"requireReturn": true,
"requireReturnType": true,
"requireParamDescription": false,
"requireReturnDescription": true
}
],
"require-jsdoc": [
"error",
{
"require": {
"FunctionDeclaration": true,
"MethodDefinition": true,
"ClassDeclaration": true
}
}
]
},
"parser": "babel-eslint",
"parserOptions": {
Expand All @@ -38,4 +62,4 @@
"allowImportExportEverywhere": true,
"ecmaVersion": 11
}
}
}
4 changes: 3 additions & 1 deletion .hound.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
eslint:
enabled: true
config_file: .eslintrc
config_file: .eslintrc.json
ignore_file: .eslintignore

fail_on_violations: true
64 changes: 64 additions & 0 deletions src/controllers/profile-settings.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import ResponseService from '../services/response.service';
import UserService from '../services/user.service';
import JwtService from '../services/jwt.service';
/**
*
*
* @class ProfileSettingsController
*/
class ProfileSettingsController {
/**
* users can create an account
* @static
* @description POST /api/view-profile
* @param {object} req request object
* @param {object} res response object
* @memberof ProfileSettingsController
* @returns {object} ResponseService
*/
static async viewProfile(req, res) {
const userToken = JwtService.verifyToken(req.token);
const userRegistered = await UserService.findUserByProperty({ email: userToken.email });

delete userRegistered.dataValues.password;
delete userRegistered.dataValues.token;
delete userRegistered.dataValues.isVerified;
delete userRegistered.dataValues.createdAt;
delete userRegistered.dataValues.updatedAt;

ResponseService.setSuccess(200, 'User Profile Found', userRegistered);
ResponseService.send(res);
}


/**
* users can create an account
* @static
* @description POST /api/edit-profile
* @param {object} req request object
* @param {object} res response object
* @memberof ProfileSettingsController
* @returns {object} ResponseService
*/
static async editProfile(req, res) {
const userToken = JwtService.verifyToken(req.token);
await Promise.all(Object.keys(req.body).map(async (key, index) => {
await UserService.updateUser(
{ email: userToken.email },
{ [key]: (Object.values(req.body)[index]).trim() }
);
}));
const userData = await UserService.findUserByProperty({ email: userToken.email });

delete userData.dataValues.password;
delete userData.dataValues.token;
delete userData.dataValues.isVerified;
delete userData.dataValues.createdAt;
delete userData.dataValues.updatedAt;

ResponseService.setSuccess(200, 'Profile Updated successfully', userData);
ResponseService.send(res);
}
}

export default ProfileSettingsController;
32 changes: 30 additions & 2 deletions src/migrations/20191218075931-create-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,37 @@ export function up(queryInterface, Sequelize) {
password: {
type: Sequelize.STRING
},
role: {
gender: {
type: Sequelize.STRING,
allowNull: true
},
birthDate: {
type: Sequelize.DATE,
allowNull: true
},
preferredLanguage: {
type: Sequelize.STRING,
defaultValue: 'english'
},
preferredCurrency: {
type: Sequelize.STRING,
defaultValue: 'Dollar'
},
residence: {
type: Sequelize.STRING,
allowNull: true
},
department: {
type: Sequelize.STRING,
defaultValue: 'Requester'
allowNull: true
},
lineManager: {
type: Sequelize.STRING,
allowNull: true
},
role: {
type: Sequelize.ENUM('super_admin', 'travel_admin', 'travel_team_member', 'manager', 'requester'),
defaultValue: 'requester',
},
isVerified: {
type: Sequelize.BOOLEAN,
Expand Down
9 changes: 8 additions & 1 deletion src/models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ export default (sequelize, DataTypes) => {
lastName: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
role: DataTypes.STRING,
gender: DataTypes.STRING,
birthDate: DataTypes.DATE,
preferredLanguage: DataTypes.STRING,
preferredCurrency: DataTypes.STRING,
residence: DataTypes.STRING,
department: DataTypes.STRING,
lineManager: DataTypes.STRING,
role: DataTypes.ENUM('super_admin', 'travel_admin', 'travel_team_member', 'manager', 'requester'),
token: DataTypes.STRING,
isVerified: DataTypes.BOOLEAN
}, {});
Expand Down
2 changes: 2 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import express from 'express';
import authRoute from './auth.route';
import tripRoute from './trip.route';
import profileSettingsRoute from './profile-settings.route';

const app = express();

app.use('/api/auth', authRoute);
app.use('/api/', tripRoute);
app.use('/api/', profileSettingsRoute);

export default app;
13 changes: 13 additions & 0 deletions src/routes/profile-settings.route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import express from 'express';
import ProfileSettingsController from '../controllers/profile-settings.controller';
import RouteAccessMiddleware from '../middlewares/route-access.middleware';
import { validateToken } from '../validations/auth.validation';
import {
validateAccountProfile
} from '../validations/account-profile.validation';

const router = express.Router();

router.get('/view-profile', RouteAccessMiddleware.checkRouteAccess, validateToken, ProfileSettingsController.viewProfile);
router.patch('/edit-profile', RouteAccessMiddleware.checkRouteAccess, validateToken, validateAccountProfile, ProfileSettingsController.editProfile);
export default router;
2 changes: 1 addition & 1 deletion src/tests/auth/signup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ describe('Test for User Signup:', () => {
res.body.data.should.have.property('firstName').equal(`${signupFixtures.firstName}`);
res.body.data.should.have.property('lastName').equal(`${signupFixtures.lastName}`);
res.body.data.should.have.property('email').equal(`${signupFixtures.email}`);
res.body.data.should.have.property('role').equal('Requester');
res.body.data.should.have.property('role').equal('requester');
res.body.data.should.have.property('isVerified').equal(false);
done();
});
Expand Down
11 changes: 11 additions & 0 deletions src/tests/fixtures/users.fixture.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const signedUpUserToken = JwtService.generateToken({ email: signupFixture
export const unregisteredEmail = {
email: 'higustave123@gmail.com'
};
export const unregisteredUserToken = JwtService.generateToken({ email: unregisteredEmail });
export const signupExpiredToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImhpZ3VzdGF2ZUBnbWFpbC5jb20iLCJpYXQiOjE1NzcxOTg5MjUsImV4cCI6MTU3NzE5ODkyNX0.cAYmYykkBtddmq7YyP3OKVrtVXwCrpxBxhFPJNDUYxE';

const payload = {
Expand Down Expand Up @@ -136,3 +137,13 @@ export const OAuthTokens = {
accessToken: 'oidhfioerhfrhfuierghfr8u438r9h34wf',
refreshToken: 'fruyfg78w43gf78w4gfwf8hw43hf89hf8',
};

export const userData = {
gender: 'M',
birthDate: '1990-10-17',
preferredLanguage: 'french',
preferredCurrency: 'Euro',
residence: 'Kigali',
department: 'IT',
lineManager: 'James Bond'
};
88 changes: 88 additions & 0 deletions src/tests/profile/profile.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import chai from 'chai';
import chaiHttp from 'chai-http';
import app from '../../app';
import {
signedUpUserToken,
wrongToken,
userData
} from '../fixtures/users.fixture';

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

describe('Test for User Account Profile:', () => {
it('It should allow user to view profile', (done) => {
chai.request(app)
.get('/api/view-profile')
.set('Authorization', signedUpUserToken)
.end((err, res) => {
res.body.should.be.an('object');
res.should.have.status(200);
res.body.should.have.property('message').equal('User Profile Found');
res.body.should.have.property('data');
res.body.data.should.have.property('firstName');
res.body.data.should.have.property('lastName');
res.body.data.should.have.property('gender');
res.body.data.should.have.property('birthDate');
res.body.data.should.have.property('preferredLanguage');
res.body.data.should.have.property('preferredCurrency');
res.body.data.should.have.property('residence');
res.body.data.should.have.property('department');
res.body.data.should.have.property('lineManager');
done();
});
});

it('It should NOT allow user to view profile: Wrong Token Provided', (done) => {
chai.request(app)
.get('/api/view-profile')
.set('Authorization', wrongToken)
.end((err, res) => {
res.body.should.be.an('object');
res.should.have.status(403);
res.body.should.have.property('message').equal('Wrong Token Provided');
done();
});
});

it('It should allow user to edit profile', (done) => {
chai.request(app)
.patch('/api/edit-profile')
.set('Authorization', signedUpUserToken, 'Accept', 'application/json')
.send(userData)
.end((err, res) => {
res.body.should.be.an('object');
res.should.have.status(200);
res.body.should.have.property('message').equal('Profile Updated successfully');
res.body.should.have.property('data');
res.body.data.should.have.property('id');
res.body.data.should.have.property('firstName');
res.body.data.should.have.property('lastName');
res.body.data.should.have.property('email');
res.body.data.should.have.property('gender');
res.body.data.should.have.property('birthDate');
res.body.data.should.have.property('preferredLanguage');
res.body.data.should.have.property('preferredCurrency');
res.body.data.should.have.property('residence');
res.body.data.should.have.property('department');
res.body.data.should.have.property('lineManager');
res.body.data.should.have.property('role');
done();
});
});
it('It should NOT allow user to edit profile: Wrong input value', (done) => {
const userGender = '';
userData.gender = userGender;
chai.request(app)
.patch('/api/edit-profile')
.set('Authorization', signedUpUserToken, 'Accept', 'application/json')
.send(userData)
.end((err, res) => {
res.body.should.be.an('object');
res.should.have.status(400);
res.body.should.have.property('message');
res.body.message.should.be.an('array');
done();
});
});
});
Loading

0 comments on commit 8b2ffc4

Please sign in to comment.