Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#167728033 Notification for approved request #55

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/controllers/TripRequestController.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import OfficeLocationRepository from '../repositories/OfficeLocationRepository';
import TripRequestRepository from '../repositories/TripRequestRepository';
import TripDestinationRespository from '../repositories/TripDestinationRepository';
import NotificationRepository from '../repositories/NotificationRepository';
import { createMessage, updateMessage } from '../utils/index';

import { sendErrorResponse, sendSuccessResponse } from '../utils/sendResponse';

Expand Down Expand Up @@ -114,6 +115,43 @@ class TripRequestController {
// return sendErrorResponse(res, 500, 'Internal Server Error');
}
}
/**
*
* @param {object} req request body
* @param {object} res response body
*/

// eslint-disable-next-line require-jsdoc
static async approveRequest(req, res) {
const { tripRequestUuid } = req.params;
// console.log(tripRequestUuid, 'dfdff');

try {
const userRole = req.userData.role;
if (userRole !== 'Manager') return sendErrorResponse(res, 403, 'Managers only are allowed to approve request');
const ManagersUuid = req.userData.uuid;
const foundTripRequest = await TripRequestRepository.findById(tripRequestUuid);
if (!foundTripRequest) return sendErrorResponse(res, 404, 'Trip request not found');
const {
// eslint-disable-next-line max-len
user_uuid: userUuid, status, request_type: requestType, travel_reasons: travelReasons, trip_plan: tripPlan
} = foundTripRequest.dataValues;
const foundUser = await UserRepository.findById(userUuid);
const managerUuid = foundUser.dataValues.manager_uuid;
const managerTable = await TripRequestRepository.findManagerById(managerUuid);
const managerUserUuid = managerTable.dataValues.user_uuid;
if (managerUserUuid !== ManagersUuid) return sendErrorResponse(res, 401, 'User\'s managers only can approve a request');
if (status !== ('pending' || 'open')) return sendErrorResponse(res, 400, 'Can only approve an open or pending request');
const message = `Your ${tripPlan} ${requestType} for ${travelReasons} has been approved`;
const currentStatus = 'unread';
const notificationType = 'tripRequest';
await updateMessage({ status: 'accepted' }, tripRequestUuid);
await createMessage(message, currentStatus, notificationType, userUuid);
return sendSuccessResponse(res, 200, message);
// eslint-disable-next-line no-empty
} catch (error) {
}
}
}

export default TripRequestController;
17 changes: 17 additions & 0 deletions src/database/seeders/20190825080303-create-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ export default {
image_url: 'http://images.com/myimagefile',
created_at: new Date(),
updated_at: new Date()
},
{
uuid: '0ee072c5-0b45-4991-b703-57a64af32da0',
email: '[email protected]',
role: 'Manager',
is_verified: true,
password: hashPassword('Bloated36'),
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
}, {
uuid: '50895de7-9ddd-4589-83e9-c4bb1cc93da7',
email: '[email protected]',
role: 'Requester',
is_verified: true,
password: hashPassword('Bloated36'),
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
}
];
return queryInterface.bulkInsert('Users', UsersData, {});
Expand Down
6 changes: 6 additions & 0 deletions src/database/seeders/20190825080304-create-managers.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ export default {
user_uuid: '95ccd25d-2524-4b95-a441-8e2643c4c079',
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
},
{
uuid: 'f973e4a0-e16e-4c59-869a-d0ee25aa1f8d',
user_uuid: '0ee072c5-0b45-4991-b703-57a64af32da0',
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
}
];
return queryInterface.bulkInsert('Managers', ManagersData, {});
Expand Down
13 changes: 13 additions & 0 deletions src/database/seeders/20190831152452-create-trip-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ export default {
return_date: '11-11-2019',
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
}, {
uuid: '16187eee-9ccd-4178-b848-4c2d468f690c',
travel_reasons: 'Business Assignment',
status: 'pending',
show_profile: false,
request_type: 'returnTrip',
trip_plan: 'multiCity',
leaving_from: 'dbf285c6-8d7c-4f71-8058-a82e22e27f6b',
return_date: '2019-10-09T23:00:00.000Z',
travel_date: '2019-10-09T23:00:00.000Z',
user_uuid: '50895de7-9ddd-4589-83e9-c4bb1cc93da7',
created_at: Sequelize.literal('NOW()'),
updated_at: Sequelize.literal('NOW()')
}
];
return queryInterface.bulkInsert('TripRequests', TripRequests, {});
Expand Down
15 changes: 15 additions & 0 deletions src/database/seeders/20190915082752-update-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

export default {
// eslint-disable-next-line arrow-parens
up: queryInterface => {
const UsersData = {
manager_uuid: 'f973e4a0-e16e-4c59-869a-d0ee25aa1f8d'
};
const condition = {
uuid: '50895de7-9ddd-4589-83e9-c4bb1cc93da7'
};
return queryInterface.bulkUpdate('Users', UsersData, condition);
},
// eslint-disable-next-line arrow-parens
down: queryInterface => queryInterface.bulkDelete('Users', null, {})
};
48 changes: 47 additions & 1 deletion src/repositories/TripRequestRepository.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable require-jsdoc */
/* eslint-disable camelcase */
/**
* @fileoverview Contains the User Auth Repository class, an interface for querying User table
Expand All @@ -9,7 +10,7 @@

import Models from '../models';

const { TripRequest, TripDestination } = Models;
const { TripRequest, TripDestination, Manager } = Models;

/**
* @description RequestRepository handles TripRequest model
Expand Down Expand Up @@ -68,6 +69,51 @@ class RequestRepository {
throw new Error(err);
}
}

/* @description findOne is a function that search for an office Location
*
* @param {object} condition limits the search of the office location
*
* @returns {object} the details of the office location that has been searched for
*/
// eslint-disable-next-line require-jsdoc
async findById(condition) {
try {
const tripRequest = await this.db.findByPk(condition);
return tripRequest;
} catch (err) {
throw new Error(err);
}
}

// eslint-disable-next-line require-jsdoc
// eslint-disable-next-line class-methods-use-this
async findManagerById(condition) {
try {
const tripRequest = await Manager.findByPk(condition);
return tripRequest;
} catch (err) {
throw new Error(err);
}
}

// eslint-disable-next-line valid-jsdoc
/**
* @description RequestRepository handles method that query our database
*
* @param {object} changes refers to the details of the user's request
*
* @returns {object} the details of the request that was created
*/
async update(changes, tripUuid) {
try {
// eslint-disable-next-line max-len
const { dataValues } = await this.db.update(changes, { returning: true, where: { uuid: tripUuid } });
return dataValues;
} catch (error) {
throw new Error(error);
}
}
}

export default new RequestRepository();
16 changes: 16 additions & 0 deletions src/repositories/UserRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,5 +262,21 @@ class UserRepository {
throw new Error(error);
}
}

/* @description findOne is a function that search for an office Location
*
* @param {object} condition limits the search of the office location
*
* @returns {object} the details of the office location that has been searched for
*/
// eslint-disable-next-line require-jsdoc
async findById(condition) {
try {
const tripRequest = await this.db.findByPk(condition);
return tripRequest;
} catch (err) {
throw new Error(err);
}
}
}
export default new UserRepository();
1 change: 1 addition & 0 deletions src/routes/trip.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ const router = Router();

router.post('/trips', authenticateUser, requestValidation.returnTrip, TripRequestController.createTripRequest);
router.get('/trips', authenticateUser, TripRequestController.tripsByUser);
router.patch('/trips/approve/:tripRequestUuid', authenticateUser, TripRequestController.approveRequest);

export default router;
17 changes: 15 additions & 2 deletions src/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// eslint-disable-next-line arrow-parens
import models from '../models';
import UserRepository from '../repositories/UserRepository';
import NotificationRepository from '../repositories/NotificationRepository';
import TripRequestRepository from '../repositories/TripRequestRepository';

export const getMIlliSeconds = date => (date ? new Date(date).getTime() : new Date().getTime());

Expand Down Expand Up @@ -32,7 +34,18 @@ const blackListThisToken = async (token) => {
token,
});
};

const createMessage = async (message, status, notificationType, userUuid) => {
const data = {
message,
status,
notification_type: notificationType,
user_uuid: userUuid,
};
NotificationRepository.create(data);
};
const updateMessage = async (changes, tripUuid) => {
TripRequestRepository.update(changes, tripUuid);
};
export {
blackListThisToken, isBlackListed
blackListThisToken, isBlackListed, createMessage, updateMessage
};
86 changes: 86 additions & 0 deletions tests/approve.trip.notifiation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@

import { describe, it } from 'mocha';
import chai, { expect } from 'chai';
import chaiHttp from 'chai-http';
import { createToken } from '../src/modules/tokenProcessor';
import app from '../src';


chai.use(chaiHttp);
describe('Approve trip request with notification', () => {
const token1 = {
uuid: '0ee072c5-0b45-4991-b703-57a64af32da0',
email: '[email protected]',
role: 'Manager'
};
const token2 = {
email: '[email protected]',
role: 'Super Administrator',
};
const token3 = {
email: '[email protected]',
role: 'Manager',
};
const UserToken = createToken(token2);
const tokenManager = createToken(token1);
const tokenManagerThree = createToken(token3);

describe('Approves User trips request PATCH /api/v1/trips/approve/', () => {
it('Should approve a user trip rquest', (done) => {
chai.request(app)
.patch('/api/v1/trips/approve/16187eee-9ccd-4178-b848-4c2d468f690c')
.set('Content-Type', 'Application/json')
.set('authorization', `Bearer ${tokenManager}`)
.end((err, res) => {
expect(res.status).eql(200);
expect(res.body.status).to.eql('success');
done();
});
});
it(' Should return an error when the user role is not a manager', (done) => {
chai.request(app)
.patch('/api/v1/trips/approve/16187eee-9ccd-4178-b848-4c2d468f690c')
.set('Content-Type', 'application/json')
.set('authorization', `Bearer ${UserToken}`)
.end((err, res) => {
expect(res.status).eql(403);
expect(res.body.status).to.eql('error');
done();
});
});
it('Should return error when the trip can not be found', (done) => {
chai.request(app)
.patch('/api/v1/trips/approve/95ccd25d-2524-4b95-a441-8e2643c4c077')
.set('Content-Type', 'application/json')
.set('authorization', `Bearer ${tokenManager}`)
.end((err, res) => {
expect(res).to.have.status(404);
expect(res.body.status).to.eql('error');
done();
});
});
it('Should return error when the user\'s manager is not the one authorizing the request', (done) => {
chai.request(app)
.patch('/api/v1/trips/approve/16187eee-9ccd-4178-b848-4c2d468f690c')
.set('Content-Type', 'application/json')
.set('authorization', `Bearer ${tokenManagerThree}`)
.end((err, res) => {
expect(res.status).to.be.eql(401);
expect(res.body.status).to.eql('error');
expect(res.body).to.have.property('error');
done();
});
});
it('Should return only pending or open trips can be approve', (done) => {
chai.request(app)
.patch('/api/v1/trips/approve/16187eee-9ccd-4178-b848-4c2d468f690c')
.set('Content-Type', 'application/json')
.set('authorization', `Bearer ${tokenManager}`)
.end((err, res) => {
expect(res.status).to.be.eql(400);
expect(res.body.status).to.eql('error');
done();
});
});
});
});
1 change: 1 addition & 0 deletions tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import './user.profile.edit.test';
import './user.profile.view.test';
import './social.login.test';
import './userRole.test';
import './approve.trip.notifiation.test';

// Request tests
import './request.test';
Expand Down