Skip to content

Commit

Permalink
feature(follow-users):Users should be able to follow each other [Fini…
Browse files Browse the repository at this point in the history
…shes #167313409]
  • Loading branch information
Noah Kalyesubula authored and Noah Kalyesubula committed Aug 15, 2019
1 parent e01eef2 commit 922e848
Show file tree
Hide file tree
Showing 11 changed files with 321 additions and 122 deletions.
197 changes: 100 additions & 97 deletions src/controllers/follow.controller.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable max-len */
import models from '../models/index';
// import sendEmail from '../services/email';
// import { newFollowerTemplate } from '../services/emailTemplates';
import newFollowerTemplate from '../services/follower.service';
import sendEmail from '../services/email';
import followhelper from '../helpers/follow.helper';

/**
Expand All @@ -26,16 +25,16 @@ class FollowController {
const { followedUser, followerUser } = helperResult;

await models.Follow.findOrCreate({
where: { followerEmail: followerUser.email, followedUserEmail: followedUser.email },
attributes: ['id', 'followerEmail', 'followedUserEmail']
where: { followerId: followerUser.id, followedUserId: followedUser.id },
attributes: ['id', 'followerId', 'followedUserId']
}).spread(async (follow, created) => {
if (created) {
// const location = req.get('host');
// const url = `${location}/api/v1/profiles/${req.user.username}`;
// const emailTemplate = newFollowerTemplate(req.user.username, url);
// eslint-disable-next-line max-len
// const message = `Hi ${username}, ${req.user.username} started following you on Authors Haven`;
// if (subscribed) sendEmail(email, `${message}`, emailTemplate);
const location = `${process.env.BACKEND_URL}/api/${process.env.API_VERSION}`;
const url = `${location}/profiles/${followerUser.username}`;
const emailTemplate = newFollowerTemplate(followerUser.username, url);
const message = `Hi ${followedUser.username}, ${followerUser.username} started following you on Authors Haven`;

sendEmail(followedUser.email, `${message}`, emailTemplate);
return res.status(200).json({ status: '200', message: `You are now following ${followedUser.username}` });
}

Expand All @@ -50,92 +49,96 @@ class FollowController {
}
}

// /**
// *
// * @description Method to fetch all users who I follow
// * @static
// * @param {object} req client request
// * @param {object} res server response
// * @returns {Object} server response object
// * @param {Function} next passes control to the next middleware
// * @memberof FollowController
// */
// static async fetchFollowing(req, res, next) {
// try {
// const usersIfollow = await Follows.findAll({
// where: { followerId: req.user.id },
// attributes: [],
// include: [
// {
// model: Users,
// attributes: [
// 'username',
// 'bio',
// 'imageUrl'
// ],
// as: 'authorDetails'
// }
// ]
// });
// if (usersIfollow.length === 0) {
// return res.status(200).json({ status: 200, message: 'You currently do not follow anyone' });
// }
// const response = usersIfollow.map(item => (
// {
// username: item.authorDetails.username,
// bio: item.authorDetails.bio,
// imageUrl: item.authorDetails.imageUrl
// }
// ));
// return res.status(200).json({ following: response, count: response.length });
// } catch (error) {
// return next(error);
// }
// }
/**
*
* @description Method to fetch all users who I follow
* @static
* @param {object} req client request
* @param {object} res server response
* @returns {Object} server response object
* @param {Function} next passes control to the next middleware
* @memberof FollowController
*/
static async listOfFollowedUsers(req, res, next) {
try {
const follower = await models.user.findOne({ where: { email: req.auth.email } });
const usersIfollow = await models.Follow.findAll({
where: { followerId: follower.id },
attributes: [],
include: [
{
model: models.user,
attributes: [
'firstname',
'lastname',
'username',
'email'
],
as: 'authorDetails'
}
]
});
if (usersIfollow.length === 0) {
return res.status(200).json({ status: 200, message: 'You currently do not follow anyone' });
}
const response = usersIfollow.map(item => (
{
username: item.authorDetails.username,
firstname: item.authorDetails.firstname,
lastname: item.authorDetails.lastname,
email: item.authorDetails.email
}
));
return res.status(200).json({ following: response, count: response.length });
} catch (error) {
return next(error);
}
}

// /**
// *
// * @description Method to fetch all users who follow me
// * @static
// * @param {object} req client request
// * @param {object} res server response
// * @returns {Object} server response object
// * @param {Function} next passes control to the next middleware
// * @memberof FollowController
// */
// static async fetchFollowers(req, res, next) {
// try {
// const myFollowers = await Follows.findAll({
// where: { authorId: req.user.id },
// attributes: [],
// include: [
// {
// model: Users,
// attributes: [
// 'email',
// 'username',
// 'bio',
// 'imageUrl'
// ],
// as: 'followerDetails'
// }
// ]
// });
// if (myFollowers.length === 0) {
// return res.status(200).json({ status: 200, message: 'You currently do not have any followers' });
// }
// const response = myFollowers.map(item => (
// {
// email: item.followerDetails.email,
// username: item.followerDetails.username,
// bio: item.followerDetails.bio,
// imageUrl: item.followerDetails.imageUrl
// }
// ));
// return res.status(200).json({ following: response, count: response.length });
// } catch (error) {
// return next(error);
// }
// }
/**
*
* @description Method to fetch all users who follow me
* @static
* @param {object} req client request
* @param {object} res server response
* @returns {Object} server response object
* @param {Function} next passes control to the next middleware
* @memberof FollowController
*/
static async listOfFollowers(req, res, next) {
try {
const followedUser = await models.user.findOne({ where: { email: req.auth.email } });
const myFollowers = await models.Follow.findAll({
where: { followedUserId: followedUser.id },
attributes: [],
include: [
{
model: models.user,
attributes: [
'firstname',
'lastname',
'username',
'email'
],
as: 'followerDetails'
}
]
});
if (myFollowers.length === 0) {
return res.status(200).json({ status: 200, message: 'You currently do not have any followers' });
}
const response = myFollowers.map(item => (
{
email: item.followerDetails.email,
username: item.followerDetails.username,
bio: item.followerDetails.bio,
imageUrl: item.followerDetails.imageUrl
}
));
return res.status(200).json({ following: response, count: response.length });
} catch (error) {
return next(error);
}
}
}
export default FollowController;
3 changes: 1 addition & 2 deletions src/helpers/follow.helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import models from '../models/index';
const util = new Util();

export default async (req, res) => {
const { email } = req.params;
const followedUser = await models.user.findOne({ where: { email } });
const followedUser = await models.user.findOne({ where: { id: req.params.userId } });
const followerUser = await models.user.findOne({ where: { email: req.auth.email } });
if (!followedUser) {
util.setError(404, 'user does not exist');
Expand Down
26 changes: 26 additions & 0 deletions src/middlewares/validators/userId.validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Joi from 'joi';
import Util from '../../helpers/util';

const util = new Util();

export default (req, res, next) => {
const { userId } = req.params;

const schema = {
userId: Joi.number().positive().required()
};
const { error } = Joi.validate({
userId
}, schema);

if (!error) return next();
const errorMessageFromJoi = error.details[0].message;
switch (errorMessageFromJoi) {
case '"userId" must be a number':
util.setError(400, 'userId must be a non negative integer');
return util.send(res);
default:
util.setError(400, errorMessageFromJoi);
return util.send(res);
}
};
8 changes: 4 additions & 4 deletions src/migrations/20190813140342-create-follow.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ module.exports = {
primaryKey: true,
type: Sequelize.INTEGER
},
followerEmail: {
type: Sequelize.STRING
followerId: {
type: Sequelize.INTEGER
},
followedUserEmail: {
type: Sequelize.STRING
followedUserId: {
type: Sequelize.INTEGER
},
createdAt: {
allowNull: false,
Expand Down
28 changes: 13 additions & 15 deletions src/models/follow.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const Follow = sequelize.define('Follow', {
followerEmail: DataTypes.STRING,
followedUserEmail: DataTypes.STRING
followerId: DataTypes.INTEGER,
followedUserId: DataTypes.INTEGER
}, {});
Follow.associate = function(models) {
Follow.associate = ({ user }) => {
// associations can be defined here
// Follow.belongsTo(models.User, {
// foreignKey: 'followerId',
// sourceKey: 'followerId',
// as: 'follower',
// onDelete: 'CASCADE',
// });
// Follow.belongsTo(models.User, {
// foreignKey: 'followedUserId',
// sourceKey: 'followedUserId',
// as: 'followedUser',
// onDelete: 'CASCADE',
// });
Follow.belongsTo(user, {
foreignKey: 'followerId',
onDelete: 'CASCADE',
as: 'authorDetails'
});
Follow.belongsTo(user, {
foreignKey: 'followedUserId',
onDelete: 'CASCADE',
as: 'followerDetails'
});
};
return Follow;
};
13 changes: 10 additions & 3 deletions src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@ module.exports = (sequelize, DataTypes) => {
role: {type:DataTypes.STRING, defaultValue: 'normal'},
verified: {type: DataTypes.BOOLEAN, defaultValue: false},
}, {});
user.associate = function(models) {
// associations can be defined here
};

user.associate = ({
Follow,

}) => {
user.hasMany(Follow, {
foreignKey: 'followerId',
as: 'followerDetails'
});
};
return user;
};
32 changes: 32 additions & 0 deletions src/routes/api/user/doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,36 @@
* description: Successfully reset your password
* 400:
* description: Validation error
* /users/profiles/{userId}/follow:
* post:
* description: follow user
* produces:
* - application/json
* parameters:
* - in: path
* name: userId
* schema:
* type: integer
* required: true
* responses:
* 200:
* description: You are now following
* 400:
* description: userId must be a non negative integer
* /users/profiles/following:
* get:
* description: get all users that i follow
* produces:
* - application/json
* responses:
* 200:
* description: You currently do not follow anyone
* /users/profiles/followers:
* get:
* description: get all users that i follow
* produces:
* - application/json
* responses:
* 200:
* description: You currently do not have any followers
*/
5 changes: 4 additions & 1 deletion src/routes/api/user/user.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import express from 'express';
import UserController from '../../../controllers/user.controller';
import validateToken from '../../../middlewares/auth';
import validateUser from '../../../middlewares/validators/signup.validation';
import validateUserId from '../../../middlewares/validators/userId.validation';
import admin from '../../../middlewares/admin';
import verifyEmail from '../../../controllers/verify-controller';
import confirmEmaiAuth from '../../../middlewares/emailVarification.middleware';
Expand All @@ -18,7 +19,9 @@ router.delete('/:id', [validateToken, confirmEmaiAuth], UserController.deleteUse
router.put('/update/:email', [validateToken, confirmEmaiAuth], UserController.updateUser);
router.post('/signup/admin', [validateToken, admin, confirmEmaiAuth], UserController.createAdmin);
router.post('/signout', validateToken, UserController.signoutUser);
router.post('/profiles/:email/follow', validateToken, followController.follow);
router.post('/profiles/:userId/follow', [validateToken, validateUserId], followController.follow);
router.get('/profiles/following', validateToken, followController.listOfFollowedUsers);
router.get('/profiles/followers', validateToken, followController.listOfFollowers);

// reset password route handlers
router.post('/reset', UserController.requestPasswordReset);
Expand Down
Loading

0 comments on commit 922e848

Please sign in to comment.