Skip to content

Commit

Permalink
ft(editRequest): user should be able to edit trip request
Browse files Browse the repository at this point in the history
- api development
- database verification
- validation
- testing api
- api documentation
- [Finishes #169258650]
  • Loading branch information
NiyoEric committed Nov 28, 2019
1 parent fe8689d commit ddf1851
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 12 deletions.
46 changes: 45 additions & 1 deletion src/controllers/TripController.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Customize from '../helpers/Customize';
import { tripRequests, users } from '../database/models';
import { tripRequests, users, trips } from '../database/models';
import ControllerHelper from '../helpers/ControllerHelper';
import emailHelper from '../helpers/EmailHelper';

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

/**
* Users should be able to edit a trip
* @static
* @param {object} req request object
* @description PUT /api/v1/trips/edit/TriprequestID
* @param {object} res response object
* @memberof UserController
* @returns {object} data
*/
static async editTrip(req, res) {
try {
const itinerary = req.body.itinerary ? req.body.itinerary : [req.body];
const Ids = await trips.findAll(
{
attributes: ['id'],
where: {
tripRequestId: req.params.id
}
}
);
await tripRequests.update(
{ statusId: 1 },
{ where: { id: req.params.id } }
);

itinerary.forEach(async (item, index) => {
const tripId = Ids[index].dataValues.id;
await trips.update({
originId: item.originId,
destinationId: item.destinationId,
reason: item.reason,
startDate: item.startDate,
returnDate: item.returnDate
},
{
where: { id: tripId }
});
});
return Customize.successMessage(req, res, 'Trip edited successfuly', itinerary, 200);
} catch (err) {
return Customize.errorMessage(req, res, err.message, 500);
}
}
}

export default TripController;
6 changes: 2 additions & 4 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 All @@ -300,9 +298,9 @@ class UserController {
where: { userId: id },
});

Customize.successMessage(req, res, 'User profile retrieved successfully', userData, HttpStatus.OK);
return Customize.successMessage(req, res, 'User profile retrieved successfully', userData, HttpStatus.OK);
} catch (e) {
Customize.errorMessage(req, res, 'Server error', HttpStatus.INTERNAL_SERVER_ERROR);
return Customize.errorMessage(req, res, 'Server error', HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions src/helpers/ControllerHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,9 @@ class ControllerHelper {
const newTrip = await tripRequests.create({
userId, statusId: 1, tripTypeId
});
const request = 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,
Expand Down
15 changes: 14 additions & 1 deletion src/middlewares/Conflict.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import DataEngine from './DataEngine';
import { cities, users } from '../database/models';
import { cities, users, tripRequests } from '../database/models';

/**
* @export
Expand Down Expand Up @@ -31,6 +31,19 @@ class Conflict {
static isUsersConflict(req, res, next) {
return DataEngine.findOne(req, res, next, users, { id: req.params.id }, 'The user already exist');
}

/**
* Check if trip requests exist
* @static
* @param {object} req request object
* @param {object} res response object
* @param {object} next next
* @memberof Conflict
* @returns {object} data
*/
static isTripRequestFound(req, res, next) {
return DataEngine.findOne(req, res, next, tripRequests, { id: req.params.id }, 'The trip request id not found');
}
}


Expand Down
37 changes: 37 additions & 0 deletions src/middlewares/Validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,42 @@ class Validate {
check('bio', 'Please your bio is needed to complete your profile(at least 15 characters)').isLength({ min: 15 })
];
}

/**
* Validate input
* @static
* @returns {object} errors
*/
static isIDInteger() {
return [
check('id', 'ID should be an integer').isInt(),
];
}

/**
* Validate input
* @static
* @returns {object} errors
*/
static editRequest() {
const d = new Date();
const year = d.getFullYear();
const month = d.getMonth();
const day = d.getDate();
const correctDate = new Date(year, month, day).toDateString();
return [
check('itinerary.*.startDate', 'Invalid Date(format: YYYY-MM-DD)').isAfter(correctDate).optional(),
check('itinerary.*.returnDate', 'Invalid Date(format: YYYY-MM-DD)').isAfter(correctDate).optional(),
check('itinerary.*.originId', 'originId should be an integer').isNumeric().optional(),
check('itinerary.*.destinationId', 'destinationId should be an integer').isNumeric().optional(),
check('itinerary.*.reason', 'reason should be a minimum of 2 letters').isString().isLength({ min: 2 }).optional(),

check('originId', 'originId should be an integer').isNumeric().optional(),
check('destinationId', 'destinationId should be an integer').isNumeric().optional(),
check('reason', 'Reason should be a minimum of 2 letters').isString().isLength({ min: 2 }).optional(),
check('startDate', 'Start date should be a valid date after today(YY-MM-DD) ').isAfter().isISO8601().optional(),
check('returnDate', 'returnDate must be valid date after today(YY-MM-DD) ').isAfter().isISO8601().optional(),
];
}
}
export default Validate;
2 changes: 1 addition & 1 deletion src/middlewares/ValidateTrip.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export default class ValidateTrip {
});

if (object.object) {
return Customize.errorMessage(req, res, 'Similar request exist', 409);
return Customize.errorMessage(req, res, 'Similar trip exists within this range.', 409);
}

next();
Expand Down
17 changes: 17 additions & 0 deletions src/middlewares/findUsers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { users } from '../database/models';
import Customize from '../helpers/Customize';

const findOneUser = async (req, res, next) => {
const { email } = req.body;
Expand All @@ -9,4 +10,20 @@ const findOneUser = async (req, res, next) => {
}
next();
};

export const IsOwnerOfTrip = async (req, res, next) => {
if (req.user.id !== req.row.userId) {
return Customize.errorMessage(req, res, 'You are not the owner of the trip', 409);
}
next();
};

export const IsTripApproved = async (req, res, next) => {
if (req.row.statusId === 2) {
return Customize.errorMessage(req, res, 'The trip is already approved', 409);
}
next();
};


export default findOneUser;
63 changes: 61 additions & 2 deletions src/routes/api/tripRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import ValidateTrip from '../../middlewares/ValidateTrip';
import Validate from '../../middlewares/Validate';
import checkInputDataError from '../../middlewares/checkInputDataError';
import Exists from '../../middlewares/Exists';

import Conflict from '../../middlewares/Conflict';
import isUserVerified from '../../middlewares/isUserVerified';
import { IsOwnerOfTrip, IsTripApproved } from '../../middlewares/findUsers';

const tripRouter = express.Router();
const { verifyToken } = AuthenticateToken;
Expand Down Expand Up @@ -172,7 +174,6 @@ tripRouter.post('/twoWay',
verifyToken,
Validate.twoWayTripRules(),
checkInputDataError,

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

tripRouter
.put(
'/edit/:id',
verifyToken,
isUserVerified,
Validate.isIDInteger(),
checkInputDataError,
Validate.editRequest(),
checkInputDataError,
Conflict.isTripRequestFound,
IsOwnerOfTrip,
IsTripApproved,
ValidateTrip.checkIfOriginDestinationExists,
ValidateTrip.checkIfOriginSameAsDestination,
ValidateTrip.checkMultiCityForSimilarRequests,
ValidateTrip.checkForSimilarRequests,
TripController.editTrip
);


/**
* @swagger
*
* /trips/edit/{id}:
* put:
* summary: User should be able to edit trip
* tags: [Trip]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TripMultiCity'
* parameters:
* - name: token
* in: header
* description: enter user token
* required: true
* schema:
* type: string
* - name: id
* in: path
* description: trip request id
* required: true
* schema:
* type: integer
* format: int64
*
* responses:
* "200":
* description: Trip edited successfuly
* "400":
* description: Bad request
* "401":
* description: Authentication error
*
*/

export default tripRouter;
67 changes: 66 additions & 1 deletion src/tests/060-tripTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import chaiHttp from 'chai-http';
import dotenv from 'dotenv';
import app from '../index';
import tripMockData from './mock/tripMockData';
import mockData from './mock/mockData';
import mockData, { superAdminToken } from './mock/mockData';


chai.use(chaiHttp);
Expand Down Expand Up @@ -214,3 +214,68 @@ describe('Request One way trip test', () => {
});
});
});

describe('Users should be able to edit trips', () => {
let tripRequestMulticityId, tripRequestOneWayId;

before((done) => {
chai.request(app)
.post('/api/v1/trips/multicity')
.set('token', superAdminToken)
.send(tripMockData.multiCityData)
.end((err, res) => {
tripRequestMulticityId = res.body.data;
done();
});
});

before((done) => {
chai.request(app)
.post('/api/v1/trips/oneway')
.set('token', superAdminToken)
.send(tripMockData.newOneWayTrip)
.end((err, res) => {
tripRequestOneWayId = res.body.data;
done();
});
});

it('User should not be able to send the same multitrip', (done) => {
chai.request(app)
.put(`/api/v1/trips/edit/${tripRequestMulticityId.id}`)
.set('token', superAdminToken)
.send(tripMockData.multiCityData)
.end((err, res) => {
res.status.should.eql(409);
res.body.should.be.an('object');
res.body.should.have.property('message', 'Similar request exist');
done();
});
});

it('User should edit multi city', (done) => {
chai.request(app)
.put(`/api/v1/trips/edit/${tripRequestMulticityId.id}`)
.set('token', superAdminToken)
.send(tripMockData.newMultiCityData)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.an('object');
res.body.should.have.property('data');
done();
});
});

it('User should edit one way', (done) => {
chai.request(app)
.put(`/api/v1/trips/edit/${tripRequestOneWayId.id}`)
.set('token', superAdminToken)
.send(tripMockData.editNewOneWay)
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.an('object');
res.body.should.have.property('data');
done();
});
});
});
32 changes: 32 additions & 0 deletions src/tests/mock/tripMockData.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,39 @@ const tripMockData = {
},
invalidToken: 'jfdgvfjfjdbfjbdjfbdjbfjdbfjdb',

newMultiCityData: {
itinerary: [
{
originId: 3,
destinationId: 5,
startDate: '2020-01-20',
returnDate: '2021-01-10',
reason: 'edit me multicity 1'
},
{
originId: 4,
destinationId: 3,
startDate: '2022-01-20',
returnDate: '2023-01-10',
reason: 'edit me multicity 2'
},
]
},
newOneWayTrip: {
originId: 3,
destinationId: 4,
startDate: '2021-11-20',
returnDate: '2022-01-10',
reason: 'edit me one-way trip'
},

editNewOneWay: {
originId: 1,
destinationId: 4,
startDate: '2022-11-20',
returnDate: '2023-01-10',
reason: 'edit me one-way trip'
}
};

export default tripMockData;

0 comments on commit ddf1851

Please sign in to comment.