Skip to content

Commit

Permalink
Merge pull request #25 from andela/ft-return-trip-request-167727733
Browse files Browse the repository at this point in the history
#167727733 Return trip request
  • Loading branch information
tejiri4 committed Aug 26, 2019
2 parents f9ceec8 + 4c0b0b9 commit 5a38c66
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 87 deletions.
3 changes: 2 additions & 1 deletion src/controllers/requestController.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@ const requestTrip = async (req, res) => {
const { body, decoded } = req;
const { id: userId } = decoded;
const {
type, originCity, destinationCity, departureDate, reason, accommodation
type, originCity, destinationCity, departureDate, returnDate, reason, accommodation
} = body;

const tripToBeRequested = {
type,
originCity,
destinationCity,
departureDate,
returnDate,
reason,
accommodation,
userId,
Expand Down
4 changes: 4 additions & 0 deletions src/database/migrations/20190820173619-create-request.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ module.exports = {
type: Sequelize.DATE,
allowNull: false,
},
returnDate: {
type: Sequelize.DATE,
allowNull: true,
},
reason: {
type: Sequelize.STRING,
allowNull: false,
Expand Down
4 changes: 4 additions & 0 deletions src/models/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ module.exports = (sequelize, DataTypes) => {
type: DataTypes.DATE,
allowNull: false,
},
returnDate: {
type: DataTypes.DATE,
allowNull: true,
},
reason: {
type: DataTypes.STRING,
allowNull: false,
Expand Down
179 changes: 98 additions & 81 deletions src/routes/api/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,105 @@ const { requestTripSchema } = requestSchema;
const requestRoute = (router) => {
router.route('/requests')
/**
* @swagger
* components:
* schemas:
* RequestTrip:
* properties:
* id:
* type: string
* readOnly: true
* type:
* type: string
* originCity:
* type: string
* destinationCity:
* type: string
* departureDate:
* type: string
* returnDate:
* type: string
* reason:
* type: string
* accommodation:
* type: string
* approvalStatus:
* type: string
* readOnly: true
* createdAt:
* type: string
* readOnly: true
* updateAt:
* type: string
* readOnly: true
* ErrorResponse:
* properties:
* status:
* type: string
* data:
* type: string
*/
* @swagger
* components:
* schemas:
* RequestTrip:
* properties:
* id:
* type: string
* readOnly: true
* type:
* type: string
* enum: [one-way, return]
* originCity:
* type: string
* example: London
* destinationCity:
* type: string
* example: Asgard
* departureDate:
* type: string
* format: date-time
* returnDate:
* type: string
* format: date-time
* reason:
* type: string
* example: Interview Loki
* accommodation:
* type: string
* example: Asgard Palace
* approvalStatus:
* type: boolean
* readOnly: true
* createdAt:
* type: string
* format: date-time
* readOnly: true
* updateAt:
* type: string
* format: date-time
* readOnly: true
* ErrorResponse:
* properties:
* status:
* type: string
* example: error
* data:
* type: string
*/

/**
* @swagger
* /api/v1/requests:
* post:
* tags:
* - Requests
* description: Create a request for a trip
* produces:
* - application/json
* requestBody:
* description: Request object that needs to be sent for approval
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/RequestTrip'
* responses:
* 201:
* description: Trip request successfully created
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: object
* data:
* $ref: '#/components/schemas/RequestTrip'
* 400:
* description: Input validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* 500:
* description: Internal Server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* security:
* - bearerAuth: []
*/
/**
* @swagger
* /api/v1/requests:
* post:
* tags:
* - Requests
* description: Create a request for a trip
* produces:
* - application/json
* requestBody:
* description: Request object that needs to be sent for approval
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/RequestTrip'
* responses:
* 201:
* description: Trip request successfully created
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* example: success
* data:
* allOf:
* - $ref: '#/components/schemas/RequestTrip'
* - type: object
* properties:
* userId:
* type: string
* format: uuid
* 400:
* description: Input validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* 500:
* description: Internal Server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* security:
* - bearerAuth: []
*/
.post(checkToken, validate(requestTripSchema), requestTrip);
};

Expand Down
27 changes: 27 additions & 0 deletions src/test/mockData/requestMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,32 @@ export default {
departureDate: '2019-08-21 17:59:04.305+00',
reason: 'Holiday',
accommodation: 'Transcorp Hotel'
},
oneWayTripRequestWithReturnDate: {
type: 'one-way',
originCity: 'Abuja',
destinationCity: 'New Delhi',
departureDate: '2019-09-21 17:59:04.305+00',
returnDate: '2020-08-21 17:59:04.305+00',
reason: 'Holiday',
accommodation: 'Transcorp Hotel'
},
validReturnTripRequest: {
type: 'return',
originCity: 'Abuja',
destinationCity: 'New Delhi',
departureDate: '2019-09-21 17:59:04.305+00',
returnDate: '2020-08-21 17:59:04.305+00',
reason: 'Holiday',
accommodation: 'Transcorp Hotel'
},
returnTripRequestWithDepartureGreaterThanReturnDate: {
type: 'return',
originCity: 'Abuja',
destinationCity: 'New Delhi',
departureDate: '2020-09-21 17:59:04.305+00',
returnDate: '2019-08-21 17:59:04.305+00',
reason: 'Holiday',
accommodation: 'Transcorp Hotel'
}
};
52 changes: 49 additions & 3 deletions src/test/routes/request.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import mockData from '../mockData';
import authHelper from '../../utils/authHelper';

const { Request } = models;
const { validTripRequest, badInputTripRequest } = mockData.requestMock;
const {
validTripRequest, badInputTripRequest, oneWayTripRequestWithReturnDate,
validReturnTripRequest, returnTripRequestWithDepartureGreaterThanReturnDate
} = mockData.requestMock;
const { generateToken } = authHelper;

describe('REQUESTS', () => {
Expand All @@ -26,7 +29,7 @@ describe('REQUESTS', () => {
.end((err, res) => {
const { data } = res.body;
expect(res.status).to.equal(201);
expect(data).to.have.property('type');
expect(data).to.have.property('type', 'one-way');
expect(data).to.have.property('originCity');
expect(data).to.have.property('destinationCity');
expect(data).to.have.property('departureDate');
Expand All @@ -36,7 +39,26 @@ describe('REQUESTS', () => {
});
});

it('should show validation error', (done) => {
it('should request a return trip successfully', (done) => {
chai.request(app)
.post(requestTripEndpoint)
.set('authorization', token)
.send(validReturnTripRequest)
.end((err, res) => {
const { data } = res.body;
expect(res.status).to.equal(201);
expect(data).to.have.property('type', validReturnTripRequest.type);
expect(data).to.have.property('originCity', validReturnTripRequest.originCity);
expect(data).to.have.property('destinationCity', validReturnTripRequest.destinationCity);
expect(data).to.have.property('departureDate');
expect(data).to.have.property('returnDate');
expect(data).to.have.property('reason', validReturnTripRequest.reason);
expect(data).to.have.property('accommodation', validReturnTripRequest.accommodation);
done(err);
});
});

it('should show missing key validation error', (done) => {
chai.request(app)
.post(requestTripEndpoint)
.set('authorization', token)
Expand All @@ -48,6 +70,30 @@ describe('REQUESTS', () => {
});
});

it('should show daparture date is greater than return date validation error', (done) => {
chai.request(app)
.post(requestTripEndpoint)
.set('authorization', token)
.send(returnTripRequestWithDepartureGreaterThanReturnDate)
.end((err, res) => {
expect(res.status).to.equal(400);
expect(res.body).to.have.property('status').that.equal('error');
done(err);
});
});

it('should show validation error', (done) => {
chai.request(app)
.post(requestTripEndpoint)
.set('authorization', token)
.send(oneWayTripRequestWithReturnDate)
.end((err, res) => {
expect(res.status).to.equal(400);
expect(res.body).to.have.property('status').that.equal('error');
done(err);
});
});

it('should return an internal server error', (done) => {
const stub = sinon.stub(Request, 'create').callsFake(() => Promise.reject(new Error('Internal server error')));
chai
Expand Down
11 changes: 9 additions & 2 deletions src/validation/requestSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ const requestTripSchema = Joi.object({
type: JoiValidator.validateString().valid('one-way', 'return').required(),
originCity: JoiValidator.validateString().required(),
destinationCity: JoiValidator.validateString().required(),
departureDate: JoiValidator.validateDate().required(),
returnDate: JoiValidator.validateDate().allow(''),
departureDate: JoiValidator.validateDate().required().when('returnDate', {
is: Joi.required(),
then: Joi.date().less(Joi.ref('returnDate')),
}),
returnDate: JoiValidator.validateDate().when('type', {
is: Joi.valid('return'),
then: Joi.required(),
otherwise: Joi.forbidden(),
}),
reason: JoiValidator.validateString().required(),
accommodation: JoiValidator.validateString().required(),
});
Expand Down

0 comments on commit 5a38c66

Please sign in to comment.