From 4fd3c36e3d3746fd60993f26e70fc75d8b6baf6c Mon Sep 17 00:00:00 2001 From: MITCHELL PATRICK Date: Fri, 6 Sep 2019 00:20:58 +0100 Subject: [PATCH] ft(feedback):get all feedback Allows a super admin/travel admin to get all feedback for an accommodation [Finishes #168250889] --- src/controllers/feedback.controller.js | 41 ++++++++ src/db/models/feedback.js | 3 +- src/middlewares/auth.middleware.js | 14 +++ src/routes/api/feedback.router.js | 10 ++ src/routes/index.js | 4 +- src/test/feedback.test.js | 125 +++++++++++++++++++++++++ swagger.json | 60 +++++++++++- 7 files changed, 250 insertions(+), 7 deletions(-) create mode 100644 src/controllers/feedback.controller.js create mode 100644 src/routes/api/feedback.router.js create mode 100644 src/test/feedback.test.js diff --git a/src/controllers/feedback.controller.js b/src/controllers/feedback.controller.js new file mode 100644 index 0000000..7dbf75d --- /dev/null +++ b/src/controllers/feedback.controller.js @@ -0,0 +1,41 @@ +import models from '../db/models'; +import Response from '../utils/response.utils'; + +const { Feedback, Facility, User } = models; + +/** + * This class controls the feedback for facilities + */ +export default class FeedbackController { + /** + * @param {Object} req Contains the param from the URL + * @param {Object} res List of responses returned to the user + * @returns {array} An array of objects containing feedback + */ + static findAll(req, res) { + Facility.findByPk(req.params.id) + .then((data) => { + if (!data) { + return Response.CustomError(res, 404, 'error', + `No facility found with ID: ${req.params.id}`, 'Consult the facility Admin'); + } + Feedback.findAll({ + where: { facility_id: req.params.id }, + attributes: ['id', 'facility_id', 'feedback', 'created_at', 'updated_at'], + include: [{ + model: User, + as: 'user', + attributes: ['id', 'first_name', 'last_name'] + }], + }) + .then((data) => { + if (data.length) { + return Response.Success(res, data, 200); + } + return Response.CustomError(res, 404, 'error', 'No feedback found for this facility', + 'Please check again later'); + }); + }) + .catch((err) => Response.InternalServerError(res, err)); + } +} diff --git a/src/db/models/feedback.js b/src/db/models/feedback.js index 531a6a3..8e9cae2 100644 --- a/src/db/models/feedback.js +++ b/src/db/models/feedback.js @@ -5,12 +5,13 @@ module.exports = (sequelize, DataTypes) => { allowNull: false } }, { - tableName: 'feedback', + tableName: 'feedbacks', underscored: true }); Feedback.associate = (models) => { Feedback.belongsTo(models.User, { foreignKey: 'user_id', + as: 'user', onDelete: 'CASCADE' }); Feedback.belongsTo(models.Facility, { diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js index 94f9eae..38f5121 100644 --- a/src/middlewares/auth.middleware.js +++ b/src/middlewares/auth.middleware.js @@ -38,4 +38,18 @@ export default { }); } }, + + isAdmin: (req, res, next) => { + const { role } = req.body; + + if (role !== 'super_admin' && role !== 'travel_admin') { + return res.status(401).json({ + status: 'error', + error: { + message: 'Auth Error: Only SuperAdmins/Travel Admins can perform this operation' + }, + }); + } + return next(); + }, }; diff --git a/src/routes/api/feedback.router.js b/src/routes/api/feedback.router.js new file mode 100644 index 0000000..16fca12 --- /dev/null +++ b/src/routes/api/feedback.router.js @@ -0,0 +1,10 @@ +import express from 'express'; +import Auth from '../../middlewares/auth.middleware'; +import FeedbackController from '../../controllers/feedback.controller'; +import Validator from '../../validation/index'; + +const router = express.Router(); + +router.get('/feedback/facility/:id', Auth.verifyToken, Auth.isAdmin, Validator.facilityID, FeedbackController.findAll); + +export default router; diff --git a/src/routes/index.js b/src/routes/index.js index 8affebc..f0ba2ef 100755 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -4,7 +4,9 @@ import CommentRoutes from './api/comment.router'; import UserRouter from './api/user.router'; import FacilitiesRouter from './api/facilities.router'; import RatingRouter from './api/rating.router'; +import FeedbackRouter from './api/feedback.router'; -const router = [RequestsRoutes, AuthRouter, UserRouter, CommentRoutes, FacilitiesRouter, RatingRouter]; +const router = [RequestsRoutes, AuthRouter, UserRouter, CommentRoutes, + FacilitiesRouter, RatingRouter, FeedbackRouter]; export default router; diff --git a/src/test/feedback.test.js b/src/test/feedback.test.js new file mode 100644 index 0000000..8f68bbf --- /dev/null +++ b/src/test/feedback.test.js @@ -0,0 +1,125 @@ +import chai from 'chai'; +import chaiHttp from 'chai-http'; +import sinon from 'sinon'; +import Sinonchai from 'sinon-chai'; +import app from '../index'; +import JWTService from '../services/jwt.service'; +import FeedbackController from '../controllers/feedback.controller'; + +chai.use(Sinonchai); +chai.use(chaiHttp); +chai.should(); + +const feedbackEndpoint = '/api/v1/feedback/facility'; + +let superAdminToken = `Bearer ${JWTService.generateToken({ id: 3, role: 'super_admin'})}`; +let travelAdminToken = `Bearer ${JWTService.generateToken({ id: 9, role: 'travel_admin'})}`; +let userToken = `Bearer ${JWTService.generateToken({ id: 1, role: 'requester'})}`; + +describe('/GET FEEDBACK', () => { + + it('should allow only logged in users', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/1`) + .set('Authorization', '') + .end((err, res) => { + res.should.have.status(403); + res.body.should.have.property('status').eql('error'); + done(); + }) + }); + + it('should allow Super Admins', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/1`) + .set('Authorization', superAdminToken) + .end((err, res) => { + res.should.have.status(200); + res.body.should.have.property('status').eql('success'); + done(); + }) + }); + + it('should allow Travel Admins', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/1`) + .set('Authorization', travelAdminToken) + .end((err, res) => { + res.should.have.status(200); + res.body.should.have.property('status').eql('success'); + done(); + }) + }); + + it('should deny users who are NOT Super Admins', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/1`) + .set('Authorization', userToken) + .end((err, res) => { + res.should.have.status(401); + res.body.should.have.property('status').eql('error'); + done(); + }) + }); + + it('should return 404 if facility does not exist', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/1000`) + .set('Authorization', superAdminToken) + .end((err, res) => { + res.should.have.status(404); + res.body.should.have.property('status').eql('error'); + done(); + }) + }); + + it('should return 404 if no feedback is found for a facility', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/3`) + .set('Authorization', superAdminToken) + .end((err, res) => { + res.should.have.status(404); + res.body.should.have.property('status').eql('error'); + done(); + }) + }); + + it('should return 200 if feedback is found for a facility', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/1`) + .set('Authorization', superAdminToken) + .end((err, res) => { + res.should.have.status(200); + res.body.should.have.property('status').eql('success'); + done(); + }) + }); + + it('should return 400 on invalid facility ID', (done) => { + chai.request(app) + .get(`${feedbackEndpoint}/a`) + .set('Authorization', superAdminToken) + .end((err, res) => { + res.should.have.status(400); + res.body.should.have.property('status').eql('error'); + done(); + }) + }); + + it('should return 500 if something goes wrong', (done) => { + const req = { + params: {} + }; + + const res = { + status() {}, + send() {} + }; + + sinon.stub(res, 'status').returnsThis(); + + FeedbackController.findAll(req, res); + (res.status).should.have.callCount(0); + done(); + }); +}); diff --git a/swagger.json b/swagger.json index 2e0d4ce..4a571f5 100644 --- a/swagger.json +++ b/swagger.json @@ -43,7 +43,7 @@ { "name": "Comment", "description": "Endpoint for Comments" - } + } ], "paths": { "/auth/signup": { @@ -240,6 +240,56 @@ } } }, + "/feedback/facility/{id}": { + "get": { + "description": "Get feedback for a facility by ID", + "summary": "Returns feedback for a single facility", + "tags": [ + "Feedback" + ], + "produces": [ + "application/json" + ], + "security": [], + "parameters": [ + { + "name": "id", + "in": "path", + "type": "integer", + "format": "int64", + "required": true, + "description": "ID of facility to check for feedback" + }, + { + "name": "Authorization", + "in": "header", + "required": true, + "type": "string", + "description": "Supply the 'Bearer '" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/Facility-Feedback" + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "401": { + "description": "Unauthorized" + }, + "404": { + "description": "Facility not found" + }, + "500": { + "description": "Internal Server Error" + } + } + } + }, "/requests/:id": { "patch": { "description": "A manager can reject a user's request application", @@ -349,7 +399,7 @@ } } }, - "/facilities/rooms": { + "/facilities/rooms": { "post": { "description": "Rooms Endpoint", "summary": "Travel_Admin/Super_Admin Can add rooms to an Acommodation Facilities", @@ -913,7 +963,7 @@ } ] } - }, + }, "postComment": { "title": "Post Comment on Request", "type": "object", @@ -948,9 +998,9 @@ "example": { "status": "success", "data": { - "message": "comment deleted successfully" + "message": "comment deleted successfully" } } } } -} \ No newline at end of file +}