Skip to content

Commit

Permalink
ft(avail request for approval): Add feature that avails requests made…
Browse files Browse the repository at this point in the history
… by users to the manager they report to

- Add get requests route
- Add validations
- Add tests
[Finishes #169258643]
  • Loading branch information
nziokaivy committed Nov 28, 2019
1 parent fe8689d commit b1d2bfa
Show file tree
Hide file tree
Showing 20 changed files with 178 additions and 75 deletions.
45 changes: 42 additions & 3 deletions src/controllers/TripController.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import Customize from '../helpers/Customize';
import { tripRequests, users } from '../database/models';
import {
tripRequests,
trips,
users,
userProfile
} from '../database/models';
import ControllerHelper from '../helpers/ControllerHelper';
import emailHelper from '../helpers/EmailHelper';


/**
* @export
* @class TripController
Expand Down Expand Up @@ -69,6 +73,41 @@ class TripController {
return Customize.errorMessage(req, res, err.message, 500);
}
}
}

/**
* Manager should be able to view approvals
* @static
* @param {object} req request object
* @param {object} res response object
* @memberof UserController
* @returns {object} data
*/
static async getRequestsByManager(req, res) {
const { id } = req.user;
const tripRequestsToSpecificManager = await userProfile.findAll({
where: { managerId: id },
include: [
{
model: users,
attributes: ['id', 'firstName', 'lastName'],
as: 'user',
include: [
{
model: tripRequests,
where: { statusId: 1 },
attributes: ['id', 'tripTypeId', 'statusId'],
include: [
{
model: trips
}
]
}
],
}
],
});
const result = tripRequestsToSpecificManager.map(request => request.user);
return Customize.successMessage(req, res, 'Requests fetched successfully', result, 200);
}
}
export default TripController;
2 changes: 0 additions & 2 deletions src/controllers/UserController.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,6 @@ class UserController {
Customize.errorMessage(req, res, 'Unable to update your profile', HttpStatus.BAD_REQUEST);
}
} catch (e) {
console.log(e);

Customize.errorMessage(req, res, e, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = {
type: Sequelize.STRING
},
managerId: {
type: Sequelize.INTEGER
type: Sequelize.INTEGER,
},
bio: {
type: Sequelize.STRING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ module.exports = {
onUpdate: 'CASCADE',
defaultValue: 1 ,
},

createdAt: {
allowNull: false,
type: Sequelize.DATE
Expand Down
2 changes: 1 addition & 1 deletion src/database/models/tripRequests.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = (sequelize, DataTypes) => {
{onUpdate: 'cascade'});
tripRequests.hasMany(
models.trips,
{foreignKey: 'id'},
{targetKey: 'tripRequestId'},
{ onDelete: 'cascade'},
{onUpdate: 'cascade'});
};
Expand Down
7 changes: 6 additions & 1 deletion src/database/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ module.exports = (sequelize, DataTypes) => {
}, {});
users.associate = function(models) {
users.hasMany(models.tripRequests,
{foreignKey: 'id'},
{targetKey: 'userId'},
{ onDelete: 'cascade'},
{onUpdate: 'cascade'}
);
users.hasOne(models.userProfile,
{foreignKey: 'id'},
{ onDelete: 'cascade'},
{onUpdate: 'cascade'}
);
};
return users;
};
15 changes: 14 additions & 1 deletion src/database/seeders/20191114145912-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
lastName: 'Doe',
email: 'demo@gmail.com',
password: '$2b$10$Q9H0gYzslg.oIvqoiWBEfesJoe.HTQT0Ezg5itN/h0a5Mf9gxWWvq',
roleId: 3,
roleId: 6,
isVerified: true,
signupType:'default',
createdAt: new Date(),
Expand All @@ -21,6 +21,7 @@ module.exports = {
password: '$2b$10$EVp9ow6OlwEldRIaXTdOB.sqG3zqyMUnOzNuAvaiVqzk6OvPcmSDC',
signupType: 'Barefoot',
isVerified: true,
roleId: 3,
createdAt: new Date(),
updatedAt: new Date()
},
Expand All @@ -30,6 +31,18 @@ module.exports = {
email: 'mytest@myemail.com',
password: '$2b$10$EVp9ow6OlwEldRIaXTdOB.sqG3zqyMUnOzNuAvaiVqzk6OvPcmSDC',
signupType: 'Barefoot',
roleId: 3,
isVerified: true,
createdAt: new Date(),
updatedAt: new Date()
},
{
firstName: 'Mary',
lastName: 'Doe',
email: 'marydoe@gmail.com',
password: 'Merry@12345',
signupType: 'Barefoot',
roleId: 3,
isVerified: true,
createdAt: new Date(),
updatedAt: new Date()
Expand Down
2 changes: 1 addition & 1 deletion src/database/seeders/20191114145913-user-profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = {
address: null,
imageURL:null,
department: null,
managerId: null,
managerId: 5,
bio: null,
createdAt: new Date(),
updatedAt: new Date()
Expand Down
2 changes: 1 addition & 1 deletion src/database/seeders/20191115043919-super-admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
{
firstName: 'Brian',
lastName: 'Maiyo',
email: 'maiyo.brian@andela.com',
email: 'manager.email@gmail.com',
password: '$2b$10$/Zh5Kwn95d/LPBwjL2VRc.Hpef/XZ2spe6U0eLksDgcxDuroB4XSq',
isVerified: true,
signupType: 'Barefoot',
Expand Down
6 changes: 3 additions & 3 deletions src/helpers/ControllerHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ class ControllerHelper {
const newTrip = await tripRequests.create({
userId, statusId: 1, tripTypeId
});
const request = await tripRequests.findOne({ where: { userId } });
await tripRequests.findOne({ where: { userId } });
itinerary.forEach(async (item) => {
await trips.create({
tripRequestId: request.dataValues.id,
tripRequestId: newTrip.dataValues.id,
originId: item.originId,
destinationId: item.destinationId,
reason: item.reason,
startDate: item.startDate,
returnDate: item.returnDate
returnDate: item.returnDate,
});
});
emailHelper.approveEmailHelper(req, process.env.MANAGER_EMAIL);
Expand Down
27 changes: 23 additions & 4 deletions src/middlewares/Exists.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import DataEngine from './DataEngine';
import { tripRequests } from '../database/models';

import { tripRequests, userProfile } from '../database/models';
import Customize from '../helpers/Customize';
/**
* @export
* @class Exists
Expand All @@ -25,7 +25,26 @@ class Exists {
'The trip is either approved rejected or it doesn\'t exist'
);
}
}


/**
* check is the request exist
* @static
* @param {object} req request object
* @param {object} res response object
* @param {object} next next
* @memberof Exists
* @returns {object} data
*/
static async getLineManager(req, res, next) {
// const { managerId } = req.body;
const { id } = req.user;
const userIdProfile = await userProfile.findOne({ where: { userId: id } });
if (!userIdProfile.managerId) {
return Customize.errorMessage(req, res, 'Please update your line manager', 404);
}
const { managerId } = userProfile;
req.body.managerId = managerId;
return next();
}
}
export default Exists;
15 changes: 1 addition & 14 deletions src/middlewares/Validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class Validate {
check('itinerary.*.originId', 'originId should be an integer').isNumeric(),
check('itinerary.*.destinationId', 'destinationId should be an integer').isNumeric(),
check('itinerary.*.reason', 'reason should be a minimum of 2 letters').isString().isLength({ min: 2 }),
// check('itinerary.*.managerId', 'Invalid Date(format: YYYY-MM-DD)').isAfter(correctDate),
];
}

Expand Down Expand Up @@ -108,20 +109,6 @@ class Validate {
];
}

/**
* Validate input
* @static
* @returns {object} errors
*/
static oneWayTripRules() {
return [
check('originId', 'originId should be an integer').isNumeric(),
check('destinationId', 'destinationId should be an integer').isNumeric(),
check('reason', 'Reason should be a minimum of 2 letters').isString().isLength({ min: 2 }),
check('startDate', 'Start date should be a valid date after today(YY-MM-DD) ').isAfter().isISO8601(),
];
}

/**
* Validate input
* @static
Expand Down
4 changes: 3 additions & 1 deletion src/middlewares/ValidateTrip.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ export default class ValidateTrip {
const userCitiesArr = [];

const AllCitiesArr = allCities.map((singleCity) => singleCity.id);
itinerary.forEach((singleCity) => userCitiesArr.push(singleCity.originId, singleCity.destinationId));
itinerary.forEach((singleCity) => userCitiesArr.push(
singleCity.originId, singleCity.destinationId
));

const cityExist = userCitiesArr.every((val) => AllCitiesArr.indexOf(val) !== -1);

Expand Down
1 change: 0 additions & 1 deletion src/middlewares/isRoledifferent.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ const isRoledifferent = async (req, res, next) => {
let status;

if (existingEmail === assignEmail) {

status = 403;
return Customize.errorMessage(req, res, 'you are not allowed to change your access', status);
}
Expand Down
35 changes: 31 additions & 4 deletions src/routes/api/tripRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@ import ValidateTrip from '../../middlewares/ValidateTrip';
import Validate from '../../middlewares/Validate';
import checkInputDataError from '../../middlewares/checkInputDataError';
import Exists from '../../middlewares/Exists';

import VerifyUserRoles from '../../middlewares/VerifyUserRoles';

const tripRouter = express.Router();
const { verifyToken } = AuthenticateToken;

const { returnTripController, OneWayTripController } = TripController;
const { returnTripController, OneWayTripController, getRequestsByManager } = TripController;

tripRouter.post('/oneway',
verifyToken,
Validate.requestOnewayTripRules(),
checkInputDataError,
Exists.getLineManager,
ValidateTrip.checkIfOriginDestinationExists,
ValidateTrip.checkIfOriginSameAsDestination,
ValidateTrip.checkMultiCityForSimilarRequests,
ValidateTrip.checkForSimilarRequests,
ValidateTrip.checkForSimilarRequestsDateRange,
OneWayTripController);


/**
* @swagger
*
Expand Down Expand Up @@ -68,6 +68,7 @@ tripRouter.post(
AuthenticateToken.verifyToken,
Validate.requestMultiTripRules(),
checkInputDataError,
Exists.getLineManager,
ValidateTrip.checkIfOriginDestinationExists,
ValidateTrip.checkIfOriginSameAsDestination,
ValidateTrip.checkMultiCityForSimilarRequests,
Expand Down Expand Up @@ -172,7 +173,7 @@ tripRouter.post('/twoWay',
verifyToken,
Validate.twoWayTripRules(),
checkInputDataError,

Exists.getLineManager,
ValidateTrip.checkIfOriginDestinationExists,
ValidateTrip.checkIfOriginSameAsDestination,
ValidateTrip.checkMultiCityForSimilarRequests,
Expand Down Expand Up @@ -239,4 +240,30 @@ tripRouter.post('/twoWay',
*
*/

tripRouter.get('/requests',
verifyToken,
VerifyUserRoles.isManager,
getRequestsByManager);

/**
* @swagger
*
* /trips/requests:
* get:
* summary: Available requests to manager for approval
* tags: [Trip]
* parameters:
* - name: token
* in: header
* description: enter token
* required: true
* schema:
* type: string
* responses:
* "200":
* description: Trip requests fetched successfuly
* "404":
* description: Trip requests are not found
*/

export default tripRouter;
Loading

0 comments on commit b1d2bfa

Please sign in to comment.