Skip to content

Commit

Permalink
feat(requestt): implement return trip endpoint
Browse files Browse the repository at this point in the history
 - update the existing test suite for this endpoint
 - update the `validation` Schema
 - validate the request body
 - update the controller class with `return` functionality
 - refactor tests to ensure all cases are passing
 - update Swagger documentation

[Finishes #167749958]
  • Loading branch information
hustlaviola committed Sep 11, 2019
1 parent f5bd611 commit 7d0785a
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 61 deletions.
8 changes: 0 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions src/config/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@
"type": "string",
"format": "date"
},
"returnDate": {
"type": "string",
"format": "date"
},
"reason": {
"type": "string"
},
Expand Down Expand Up @@ -242,13 +246,17 @@
"destination": {
"type": "string"
},
"type": {
"type": "string",
"enum": ["one-way", "return"]
},
"departureDate": {
"type": "string",
"format": "date"
},
"type": {
"returnDate": {
"type": "string",
"enum": ["one-way", "return"]
"format": "date"
},
"reason": {
"type": "string"
Expand Down
4 changes: 2 additions & 2 deletions src/controllers/RequestController.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export default class RequestController {
static bookTrip(req, res) {
const userId = req.user.id;
const {
origin, destination, departureDate, reason, accommodation, type
origin, destination, departureDate, reason, accommodation, type, returnDate
} = req.body;
let request = {
origin, destination, departureDate, reason, accommodation, userId, type
origin, destination, departureDate, reason, accommodation, userId, type, returnDate
};
request = Helper.formatRequest(request);
RequestService.bookTrip(request).then(response => {
Expand Down
21 changes: 0 additions & 21 deletions src/database/migrations/20190905060842-create-request.js

This file was deleted.

13 changes: 9 additions & 4 deletions src/middlewares/requestValidations.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,15 @@ import Helper from '../utils/Helper';
*/
const validateTripRequest = (req, res, next) => {
const userId = req.user.id;
let { departureDate } = req.body;
const { type } = req.body;
let { departureDate, returnDate } = req.body;
departureDate = new Date(departureDate).toISOString();
if (type === 'return') returnDate = new Date(returnDate).toISOString();
models.Request.findAll({ where: { userId } }).then(data => {
const conflicts = Helper.checkTrip(data, departureDate);
return conflicts;
if (data.length > 0) {
const conflicts = Helper.checkTrip(data, departureDate, returnDate);
return conflicts;
}
}).then(messages => {
if (messages && messages.length > 0) {
Responses.setError(409, 'you already have a trip booked around this pe'
Expand All @@ -41,10 +45,11 @@ const validateTripRequest = (req, res, next) => {
* @returns {object} JSON response
*/
const validateTrip = (req, res, next) => {
const { type } = req.body;
let { departureDate, returnDate } = req.body;
departureDate = new Date(departureDate).toISOString();
returnDate = returnDate ? returnDate.trim() : undefined;
if (returnDate) {
if (type === 'one-way' && returnDate) {
Responses.setError(400, 'you cannot have returnDate for a one-way trip');
return Responses.send(res);
}
Expand Down
5 changes: 3 additions & 2 deletions src/routes/userRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,19 @@ import userValidations from '../middlewares/userValidations';

const userRoute = express.Router();
const { validate } = validation;
const { emailExists, validateLogin } = userValidations;

userRoute.post(
'/users/signup',
validate('signup'),
userValidations.emailExists,
emailExists,
UserController.signup
);

userRoute.post(
'/users/signin',
validate('signin'),
userValidations.validateLogin,
validateLogin,
UserController.signin
);

Expand Down
24 changes: 13 additions & 11 deletions src/tests/request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ describe('/POST Requests route', () => {
.send({
origin: '',
destination: ' London ',
type: 'return',
type: 'one-wy',
departureDate: '2019/160/11',
reason: ' dgfgfg hfhfhf kfkfkf ',
accommodation: ' bbbv '
Expand All @@ -91,7 +91,7 @@ describe('/POST Requests route', () => {
});
});

it('should return an error if departure date has gone by already', done => {
it('should return an error if one-way trip has return date', done => {
chai
.request(app)
.post('/api/v1/requests')
Expand All @@ -100,7 +100,8 @@ describe('/POST Requests route', () => {
origin: 'Lagos',
destination: ' London ',
type: 'one-way',
departureDate: '2019-08-11',
departureDate: '2019-10-11',
returnDate: '2019-11-11',
reason: ' dgfgfg hfhfhf kfkfkf ',
accommodation: ' bbbv '
})
Expand All @@ -109,12 +110,12 @@ describe('/POST Requests route', () => {
expect(res.body).to.be.an('object');
expect(res.body).to.have.property('status').eql('error');
expect(res.body).to.have.property('message')
.eql('you cannot go back in time');
.eql('you cannot have returnDate for a one-way trip');
done(err);
});
});

it('should return an error if one-way trip has return date', done => {
it('should return an error if departure date has gone already', done => {
chai
.request(app)
.post('/api/v1/requests')
Expand All @@ -123,8 +124,7 @@ describe('/POST Requests route', () => {
origin: 'Lagos',
destination: ' London ',
type: 'one-way',
departureDate: '2019-10-11',
returnDate: '2019-11-11',
departureDate: '2019-08-11',
reason: ' dgfgfg hfhfhf kfkfkf ',
accommodation: ' bbbv '
})
Expand All @@ -133,7 +133,7 @@ describe('/POST Requests route', () => {
expect(res.body).to.be.an('object');
expect(res.body).to.have.property('status').eql('error');
expect(res.body).to.have.property('message')
.eql('you cannot have returnDate for a one-way trip');
.eql('you cannot go back in time');
done(err);
});
});
Expand All @@ -146,16 +146,17 @@ describe('/POST Requests route', () => {
.send({
origin: 'Lagos ',
destination: ' London ',
type: 'one-way',
type: 'return',
departureDate: '2019-10-11',
returnDate: '2019-11-11',
reason: ' dgfgfg hfhfhf kfkfkf ',
accommodation: ' bbbv '
})
.end((err, res) => {
expect(res).to.have.status(201);
expect(res.body).to.be.an('object');
expect(res.body.data).to.have.property('origin');
expect(res.body.data).to.have.property('departureDate');
expect(res.body.data).to.have.property('returnDate');
expect(res.body).to.have.property('message')
.eql('travel request booked successfully');
done(err);
Expand All @@ -170,8 +171,9 @@ describe('/POST Requests route', () => {
.send({
origin: 'Lagos ',
destination: ' London ',
type: 'one-way',
type: 'return',
departureDate: '2019-10-11',
returnDate: '2019-11-11',
reason: ' dgfgfg hfhfhf kfkfkf ',
accommodation: ' bbbv '
})
Expand Down
27 changes: 19 additions & 8 deletions src/utils/Helper.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable require-jsdoc */
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import Responses from './Responses';
Expand Down Expand Up @@ -109,8 +110,9 @@ export default class Helper {
*/
static formatRequest(request) {
const {
origin, destination, reason, accommodation, type
origin, destination, reason, accommodation, type, returnDate
} = request;
if (!returnDate) request.returnDate = undefined;
request.origin = origin.trim();
request.destination = destination.trim();
request.type = type.trim();
Expand All @@ -123,12 +125,13 @@ export default class Helper {
* @method noReturn
* @param {object} depart - Departure date from the database
* @param {object} travel - Departure date in the request body
* @param {object} back - Return date in the request body
* @returns {object} JSON response
* @memberof Helper
*/
static noReturn(depart, travel) {
static noReturn(depart, travel, back) {
const conflicts = [];
if (depart === travel) {
if (depart === travel || back === travel) {
conflicts.push(depart);
}
return conflicts;
Expand All @@ -139,13 +142,20 @@ export default class Helper {
* @param {object} travel - Departure date in the request body
* @param {object} depart - Departure date from the database
* @param {object} ret - Return date from the database
* @param {object} back - Return date in the request body
* @returns {object} JSON response
* @memberof Helper
*/
static withReturn(travel, depart, ret) {
static withReturn(travel, depart, ret, back) {
const conflicts = [];
ret = ret.toISOString();
if (travel >= depart && travel <= ret) conflicts.push(depart);
const firstConflict = travel >= depart && travel <= ret;
const secondConflict = back >= depart && back <= ret;
const thirdConflict = depart > travel && depart < back;
const fourthConflict = ret > travel && ret < back;
const firstSecondConflict = firstConflict || secondConflict;
const thirdFourthConflict = thirdConflict || fourthConflict;
if (firstSecondConflict || thirdFourthConflict) conflicts.push(depart);
return conflicts;
}

Expand All @@ -154,17 +164,18 @@ export default class Helper {
* @description Check for trip conflicts
* @param {object} myRequests - Array of user's request
* @param {object} travelDate - Departure date in the request body
* @param {object} backDate - Return date in the request body
* @returns {object} JSON response
* @memberof Helper
*/
static checkTrip(myRequests, travelDate) {
static checkTrip(myRequests, travelDate, backDate = undefined) {
let conflicts;
myRequests.forEach(request => {
let { departureDate } = request;
const { returnDate } = request;
departureDate = departureDate.toISOString();
if (!returnDate) conflicts = Helper.noReturn(departureDate, travelDate);
else conflicts = Helper.withReturn(travelDate, departureDate, returnDate);
if (!returnDate) conflicts = Helper.noReturn(departureDate, travelDate, backDate);
else conflicts = Helper.withReturn(travelDate, departureDate, returnDate, backDate);
});
if (conflicts) return conflicts;
return null;
Expand Down
14 changes: 11 additions & 3 deletions src/utils/validationSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const password = Joi.string().required().regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(

const required = Joi.string().trim().required();
const str = Joi.string().allow('');
const date = Joi.date().iso();

export default {
signup: Joi.object().keys({
Expand All @@ -34,10 +35,17 @@ export default {
request: Joi.object().keys({
origin: required.label('origin is required'),
destination: required.label('destination is required'),
type: required.valid('one-way')
.label('type is required and can only be "one-way"'),
departureDate: Joi.date().iso().required()
type: required.valid('one-way', 'return')
.label('type is required and can either be "one-way" or "return"'),
departureDate: date.required()
.label('departureDate is required and must follow this format: YYYY-MM-DD'),
returnDate: date.when('type', {
is: 'return', then: date.min(Joi.ref('departureDate')).required()
})
.concat(date.when('type', { is: 'one-way', then: date.allow('') }))
.label('returnDate is required for a "return" trip,'
+ ' it cannot come before departureDate'
+ ' and must follow this format: YYYY-MM-DD'),
reason: str.label('reason must be a string'),
accommodation: str.label('accommodation must be a string')
})
Expand Down

0 comments on commit 7d0785a

Please sign in to comment.