Skip to content
This repository has been archived by the owner on Jul 20, 2020. It is now read-only.

Commit

Permalink
feature(trip): request return trip
Browse files Browse the repository at this point in the history
- add the request return trip controller and route
- added test for the feature
- add the protect routes middleware
- changed the reset route to follow conventions
- modified the reset feature swagger documentation

[Maintains #170947553]
  • Loading branch information
izzett222 committed Feb 20, 2020
1 parent f5ba9d7 commit a2eafc8
Show file tree
Hide file tree
Showing 16 changed files with 443 additions and 100 deletions.
48 changes: 48 additions & 0 deletions src/controllers/tripsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,52 @@ export default class requestController {
return Response.errorResponse(res, 500, error.message);
}
}

/**
* @param {object} req
* @param {object} res
* @param {object} next
* @return {object} request
*/
static async createReturnRequest(req, res) {
const { user } = req;
const {
location, destination, departureDate, returnDate, reason, accomodation
} = req.body;
if (Date.parse(departureDate) > Date.parse(returnDate)) {
return Response.errorResponse(res, 400, res.__('the return date must be greater than departure date'));
}
try {
const request = await db.Request.findOne({
where: {
email: user.email,
departureDate
}
});
if (request) return Response.errorResponse(res, 400, res.__('request with the same departure date exist'));
const facility = await db.Facilities.findOne({
where: {
id: accomodation,
location: destination
}
});
if (!facility) {
return Response.errorResponse(res, 404, res.__('the accomondation doesn\'t exist or it\'s not in your destination'));
}
const newRequest = await db.Request.create({
id: uuid(),
email: user.email,
location,
destination,
departureDate,
returnDate,
reason,
accomodation,
status: 'open'
});
return Response.success(res, 201, res.__('Request created successfully'), newRequest);
} catch (err) {
return Response.errorResponse(res, 500, err.message);
}
}
}
55 changes: 55 additions & 0 deletions src/middlewares/protectRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import db from '../models';
import Response from '../utils/ResponseHandler';
import { verifyToken } from '../utils/tokenHandler';

/**
* @description protect route class
* @class protectRoutes
*/
export default class protectRoutes {
/** @description validate if user is signup
* @static
* @param {object} req
* @param {object} res
* @param {object} next
* @returns {object} next
* @memberof protectRoutes
*/
static async verifyUser(req, res, next) {
try {
const { token } = req.headers;
if (!token) {
return Response.errorResponse(res, 401, res.__('No token provided'));
}
const payload = verifyToken(token);
const user = await db.User.findOne({
where: {
id: payload.id
}
});
if (!user) {
return Response.errorResponse(res, 401, res.__('you are not authorised for this operation'));
}
req.payload = payload;
req.user = user;
return next();
} catch (err) {
if (err.name === 'JsonWebTokenError') {
return Response.errorResponse(res, 400, res.__('token must be provided and valid'));
}
return Response.errorResponse(res, 500, res.__('server error'));
}
}

/**
* @param {object} req
* @param {object} res
* @param {object} next
* @returns {object} user
*/
static async verifyRequester(req, res, next) {
const { user } = req;
if (user.role !== 'requester') Response.errorResponse(res, 401, res.__('you are not authorised for this operation'));
next();
}
}
40 changes: 0 additions & 40 deletions src/middlewares/verifyUser.js

This file was deleted.

5 changes: 3 additions & 2 deletions src/migrations/20200209182554-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = {
type: Sequelize.STRING,
defaultValue: 'none',
allowNull: false
},
},
oAuthId: {
type: Sequelize.STRING,
defaultValue: 'none'
Expand Down Expand Up @@ -60,6 +60,7 @@ module.exports = {
allowNull: false,
type: Sequelize.DATE
},

updatedAt: {
allowNull: false,
type: Sequelize.DATE
Expand All @@ -69,4 +70,4 @@ module.exports = {
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
};
35 changes: 35 additions & 0 deletions src/migrations/20200220102000-create-facilities.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Facilities', {
id: {
allowNull: false,
type: Sequelize.STRING,
primaryKey: true
},
facilityName: {
type: Sequelize.STRING,
},
location: {
type: Sequelize.STRING
},
image: {
type: Sequelize.STRING
},
numOfRooms: {
type: Sequelize.INTEGER,
defaultValue: 0
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Facilities');
}
};
7 changes: 3 additions & 4 deletions src/routes/authRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import validationResult from '../validation/validationResult';
import verificationController from '../controllers/verificationController';
import validateParams from '../validation/validateParams';
import { signupInputRules, resetPasswordRules, forgotPasswordRules } from '../validation/validationRules';

import { verifyUser } from '../middlewares/verifyUser';
import protectRoute from '../middlewares/protectRoute';

const authRouter = express.Router();

Expand All @@ -15,8 +14,8 @@ authRouter.get('/verification', validateParams.validateToken, verificationContro
authRouter.post('/login', AuthController.login);
authRouter.get('/logout', AuthController.logout);

authRouter.put('/forgotPassword', forgotPasswordRules, validationResult, AuthController.forgotPassword);
authRouter.put('/resetpassword', verifyUser, resetPasswordRules, validationResult, AuthController.resetPassword);
authRouter.put('/password/forgot', forgotPasswordRules, validationResult, AuthController.forgotPassword);
authRouter.put('/password/reset', protectRoute.verifyUser, resetPasswordRules, validationResult, AuthController.resetPassword);
authRouter.get('/google', passport.authenticate('google', {
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
Expand Down
9 changes: 4 additions & 5 deletions src/routes/tripsRoutes.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import express from 'express';
import tripsController from '../controllers/tripsController';
import { requestRules } from '../validation/validationRules';
import { requestRules, returnTripRules } from '../validation/validationRules';
import validationResult from '../validation/validationResult';
import { verifyRequester } from '../middlewares/verifyUser';
import { decode } from '../utils/tokenHandler';
import protectRoute from '../middlewares/protectRoute';

const router = express.Router();

router.post('/one-way', decode, verifyRequester, requestRules, validationResult, tripsController.createRequest);

router.post('/one-way', protectRoute.verifyUser, protectRoute.verifyRequester, requestRules, validationResult, tripsController.createRequest);
router.post('/return', protectRoute.verifyUser, protectRoute.verifyRequester, returnTripRules, validationResult, tripsController.createReturnRequest);
export default router;
22 changes: 22 additions & 0 deletions src/seeders/20200212121323-User.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,28 @@ module.exports = {
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: uuid(),
firstName: 'devrepublicc',
lastName: 'devrpore',
email: 'jeannette@andela.com',
password: bcrypt.hashSync('Bien@BAR789', Number(process.env.passwordHashSalt)),
isVerified: false,
role: 'requester',
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: '0119b84a-99a4-41c0-8a0e-6e0b6c385165',
firstName: 'Muhoza',
lastName: 'devrpore',
email: 'umuhozad@andela.com',
password: bcrypt.hashSync('Bien@BAR789', Number(process.env.passwordHashSalt)),
isVerified: false,
role: 'manager',
createdAt: new Date(),
updatedAt: new Date(),
},
{
id: uuid(),
firstName: 'devrepubli',
Expand Down
4 changes: 1 addition & 3 deletions src/services/authService.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import dotenv from 'dotenv';
import { provideToken } from '../utils/tokenHandler';
import sendMsg from '../utils/sendEmail';
import Models from '../models';

Expand All @@ -17,8 +16,7 @@ export default class AuthService {
* @returns {object} send resent password Email function
*/
static async forgotPassword(user, content) {
const token = provideToken(user.id, user.isVerified);
const link = `http://${process.env.BASE_URL}/api/v1/auth/resetPassword?token=${token}`;
const link = `http://${process.env.BASE_URL}/api/v1/auth/password/reset`;
return sendMsg(user.email, user.firstName, content, link);
}

Expand Down
46 changes: 17 additions & 29 deletions src/services/localesServices/locales/en.json
Original file line number Diff line number Diff line change
@@ -1,36 +1,16 @@
{
"Welcome to devRepublic Barefoot Nomad API": "Welcome to devRepublic Barefoot Nomad API",
"register": "register",
"signup": "signup",
"login": "login",
"logout": "logout",
"User is successfully logged out": "User is successfully logged out",
"There is no such user": "There is no such user",
"you are not authorised for this operation": "you are not authorised for this operation",
"User roles updated successfully": "User roles updated successfully",
"The user doesn't exist": "The user doesn't exist",
"The email is required": "The email is required",
"Invalid value": "Invalid value",
"manager": "manager",
"travel team member": "travel team member",
"requester": "requester",
"travel administrator": "travel administrator",
"super administrator": "super administrator",
"the acceptable roles are manager, travel team member, requester, travel administrator, super administrator": "the acceptable roles are manager, travel team member, requester, travel administrator, super administrator",
"The user is already a %s": "The user is already a %s",
"User is successfully logged in": "User is successfully logged in",
"Incorrect email or password": "Incorrect email or password",
"User is successfully registered": "User is successfully registered",
"Email already exists": "Email already exists",
"Firstname must be atleast 4 characters": "Firstname must be atleast 4 characters",
"Login first or create an account if you do not have one": "Login first or create an account if you do not have one",
"User is successfully logged in": "User is successfully logged in",
"Incorrect email or password": "Incorrect email or password",
"User is successfully logged out": "User is successfully logged out",
"Welcome to devRepublic Barefoot Nomad API": "Welcome to devRepublic Barefoot Nomad API",
"The email field must contain a valid email address": "The email field must contain a valid email address",
"At least 8 characters include symbols, uppercase, lowercase and number": "At least 8 characters include symbols, uppercase, lowercase and number",
"user not found on reset": "user not found, check your input for any mistake",
"check your email to reset your password": "check your email to reset your password",
"At least 8 characters include symbols, uppercase, lowercase and number": "At least 8 characters include symbols, uppercase, lowercase and number",
"password reset successfully": "password reset successfully",
"server error": "server error",
"you are not allowed to perform this action": "you are not allowed to perform this action",
"you are not authorised for this operation": "you are not authorised for this operation",
"token must be provided and valid": "token must be provided and valid",
"Lastname must be atleast 4 characters": "Lastname must be atleast 4 characters",
"email intro": "welcome back to barefoot nomad",
Expand All @@ -48,7 +28,6 @@
"Your image has been uploded successfully": "Your image has been uploded successfully",
"Facility created successfully": "Facility created successfully",
"Room created successfully": "Room created successfully",
"WHERE parameter \"email\" has invalid \"undefined\" value": "WHERE parameter \"email\" has invalid \"undefined\" value",
"Cannot read property %s of undefined": "Cannot read property %s of undefined",
"admin is not defined": "admin is not defined",
"column \"id\" of relation \"Facilities\" does not exist": "column \"id\" of relation \"Facilities\" does not exist",
Expand All @@ -67,5 +46,14 @@
"the accomodation doesn't exist or it's not in your destination": "the accomodation doesn't exist or it's not in your destination",
"Only USD, EUR or RWF currencies are allowed": "Only USD, EUR or RWF currencies are allowed",
"Please choose correct department": "Please choose correct department",
"enter vaid date": "enter vaid date"
}
"enter vaid date": "enter vaid date",
"the accomondation doesn't exist or it's not in your destination": "the accomondation doesn't exist or it's not in your destination",
"Request created successfully": "Request created successfully",
"request with the same departure date exist": "request with the same departure date exist",
"the return date must be greater than departure date": "the return date must be greater than departure date",
"User roles updated successfully": "User roles updated successfully",
"Invalid value": "Invalid value",
"dstination should only be letter": "dstination should only be letter",
"destination should only be letter": "destination should only be letter",
"No token provided": "No token provided"
}
13 changes: 9 additions & 4 deletions src/services/localesServices/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,18 @@
"you have to be a travel admin to perform this action": "vous devez être un administrateur de voyage pour effectuer cette action",
"column \"facilityId\" of relation \"Facilities\" does not exist": "la colonne \"facilityId \" de la relation \"Facilities \" n'existe pas",
"null value in column \"id\" violates not-null constraint": "la valeur nulle dans la colonne \"id \" viole la contrainte non nulle",
"The firstname is required": "Le prénom est obligatoire",
"First name should be only characters": "Le prénom ne doit être composé que de caractères",
"Last name should be only characters": "Le nom de famille ne doit être composé que de caractères",
"location is required": "l'emplacement est requis",
"destination is required": "destination is required",
"reason is required": "la raison est requise",
"reason is required": "la destination est obligatoire",
"accomodation is required": "l'hébergement est requis",
"departureDate is required": "date de départ obligatoire",
"enter valid date with YYYY-MM-DD format": "entrez une date valide au format AAAA-MM-JJ",
"The firstname is required": "Le prénom est obligatoire",
"First name should be only characters": "Le prénom ne doit être composé que de caractères",
"Last name should be only characters": "Le nom de famille ne doit être composé que de caractères"
"No token provided": "Aucun jeton fourni",
"the return date must be greater than departure date": "la date de retour doit être supérieure à la date de départ",
"request with the same departure date exist": "demande avec la même date de départ existe",
"the accomondation doesn't exist or it's not in your destination": "le logement n'existe pas ou n'est pas dans votre destination",
"Request created successfully": "Demande créée avec succès"
}
Loading

0 comments on commit a2eafc8

Please sign in to comment.