Skip to content

Commit

Permalink
feat(most destination): users should see most traveled destinations
Browse files Browse the repository at this point in the history
- create migration file
- create model
- create route and add to index route
- create controller
- create services file
- add services to request controller
- test created endpoints

[Finishes #167727759]
  • Loading branch information
dinorhythms committed Sep 5, 2019
1 parent cba3570 commit 783071b
Show file tree
Hide file tree
Showing 9 changed files with 254 additions and 0 deletions.
33 changes: 33 additions & 0 deletions src/controllers/mostDestinationController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import models from '../models';
import response from '../utils/response';
import DbServices from '../services/dbServices';
import messages from '../utils/messages';

const { Mostdestination } = models;
const { serverError } = messages;
const { getAllRecord } = DbServices;

/**
* get top 10 most travelled destination counters in controller
* @param {Object} req - server request
* @param {Object} res - server response
* @returns {Object} - custom response
*/
const getMostDestinations = async (req, res) => {
try {
const options = {
limit: 10,
order: [
['count', 'ASC']
]
};
const mostDestinations = await getAllRecord(Mostdestination, options);
return response(res, 200, 'success', mostDestinations);
} catch (error) {
return response(res, 500, 'error', serverError);
}
};

export default {
getMostDestinations
};
4 changes: 4 additions & 0 deletions src/controllers/requestController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import findRequest from '../services/requestServices';
import messages from '../utils/messages';
import { findById } from '../services/userServices';
import { createNotification } from '../services/notificationServices';
import addOrCreateCounter from '../services/mostDestinationServices';

const { Request, Subrequest } = models;
const {
Expand Down Expand Up @@ -149,6 +150,9 @@ const updateApprovalStatus = async (req, res) => {
}
const updateColumn = { approvalStatus: approvalStatusValue };
await update(Request, updateColumn, options);
if (approvalStatusValue === 'accepted') {
await addOrCreateCounter(requestId);
}
return response(res, 201, 'success', {
message: approvalStatusMessage
});
Expand Down
35 changes: 35 additions & 0 deletions src/database/migrations/20190904175236-create-most-destination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';

module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Mostdestinations', {
id: {
allowNull: false,
primaryKey: true,
type: Sequelize.UUID,
},
count: {
type: Sequelize.INTEGER,
allowNull: false,
},
destinationCity: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: true,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('NOW')
},
updatedAt: {
allowNull: true,
type: Sequelize.DATE,
defaultValue: Sequelize.fn('NOW')
}
});
},

down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Mostdestinations');
}
};
20 changes: 20 additions & 0 deletions src/models/mostdestination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = (sequelize, DataTypes) => {
const Mostdestination = sequelize.define('Mostdestination', {
id: {
allowNull: false,
primaryKey: true,
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
},
count: {
type: DataTypes.INTEGER,
allowNull: false,
},
destinationCity: {
type: DataTypes.STRING,
allowNull: false,
},
});

return Mostdestination;
};
76 changes: 76 additions & 0 deletions src/routes/api/mostdestination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import mostDestinationController from '../../controllers/mostDestinationController';
import { checkToken } from '../../middlewares/userMiddlewares';

const { getMostDestinations } = mostDestinationController;

const counterRoutes = (router) => {
router.route('/mostdestination')
/**
* @swagger
* components:
* schemas:
* MostDestination:
* properties:
* id:
* type: string
* readOnly: true
* count:
* type: integer
* destinationCity:
* type: string
* 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: object
*/
/**
* @swagger
* /api/v1/mostdestination:
* get:
* tags:
* - Most Travelled
* description: Get 10 most travelled destinations
* produces:
* - application/json
* responses:
* 200:
* description: Get 10 most travelled destinations successfully
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* example: success
* data:
* type: array
* description: array of chats
* items:
* $ref: '#/components/schemas/MostDestination'
* 403:
* description: Unauthorized
* 500:
* description: Internal Server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ErrorResponse'
* security:
* - bearerAuth: []
*/
.get(checkToken, getMostDestinations);
};

export default counterRoutes;
3 changes: 3 additions & 0 deletions src/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import notificationRoutes from './api/notification';
import authRoute from './api/auth';
import chatRoutes from './api/chat';
import accommodationRoute from './api/accommodation';
import mostDestinationRoutes from './api/mostdestination';

const routes = (router) => {
router
Expand Down Expand Up @@ -54,6 +55,8 @@ const routes = (router) => {
chatRoutes(router);
// accommodation routes
accommodationRoute(router);
// Most Destination routes
mostDestinationRoutes(router);
};

export default routes;
17 changes: 17 additions & 0 deletions src/services/mostDestinationServices.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import models from '../models';

const { Request, Mostdestination } = models;
/** helper function that adds request destinationCity count
* @param {String} requestId - request id for the destination
* @returns {Promise} Promise resolved or rejected
*/
const addOrCreateCounter = async (requestId) => {
const requestApproved = await Request.findByPk(requestId);
const destinationCity = requestApproved.destinationCity.toLowerCase();
const toBeUpdated = await Mostdestination.findOrCreate({
where: { destinationCity }, defaults: { count: 0 }
});
await toBeUpdated[0].increment('count');
};

export default addOrCreateCounter;
3 changes: 3 additions & 0 deletions src/test/mockData/requestMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,8 @@ export default {
},
requestToBeRejected: {
requestId: 'b2092fb0-502a-4105-961f-2d310d340168'
},
requestToBeAccepted: {
requestId: 'b2092fb0-502a-4105-961f-2d310d340168'
}
};
63 changes: 63 additions & 0 deletions src/test/routes/mostdestination..spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import {
app, chai, expect, sinon, BACKEND_BASE_URL
} from '../testHelpers/config';
import mockData from '../mockData';
import models from '../../models';
import authHelper from '../../utils/authHelper';

const { generateToken } = authHelper;
const { User, Mostdestination } = models;

// const {
// requestToBeAccepted
// } = mockData.requestMock;

const { requestMock: { requestToBeAccepted }, userMock } = mockData;
let validManagerToken;
const acceptRequestTripEndpoint = `${BACKEND_BASE_URL}/requests/accept/${requestToBeAccepted.requestId}`;

const user = { data: {}, token: '' };

before(async () => {
user.data = await User.findOne();
user.token = `Bearer ${generateToken({ id: user.data.id })}`;
validManagerToken = generateToken({ id: `${userMock.validLineManager}` });
});

describe('Most Traveled Destinations', () => {
describe('GET /mostdestination', () => {
const endpoint = `${BACKEND_BASE_URL}/mostdestination`;
it('should return top 10 traveled destination', (done) => {
chai
.request(app)
.get(endpoint)
.set('authorization', user.token)
.end((err, res) => {
expect(res.status).to.equal(200);
expect(res.body).to.have.property('status').that.equals('success');
done(err);
});
});
it('should return an internal server error', (done) => {
const stub = sinon.stub(Mostdestination, 'findAll').callsFake(() => Promise.reject(new Error('Internal server error')));
chai
.request(app)
.get(endpoint)
.set('authorization', user.token)
.end((err, res) => {
expect(res.status).to.equal(500);
expect(res.body).to.have.property('status').that.equal('error');
done(err);
stub.restore();
});
});
});
describe('POST /requests/accept/requestId', () => {
it('should return 201 response when trip is accepted and most traveled destination updated', async () => {
const response = await chai.request(app)
.patch(acceptRequestTripEndpoint)
.set('Authorization', `Bearer ${validManagerToken}`);
expect(response.status).to.equal(201);
});
});
});

0 comments on commit 783071b

Please sign in to comment.