Skip to content

Commit

Permalink
Merge pull request #22 from andela/ft-user-can-follow-166816111
Browse files Browse the repository at this point in the history
#166816111 User follow another user
  • Loading branch information
nedemenang committed Jul 10, 2019
2 parents 4fb41e4 + 2bdab6f commit e01f26d
Show file tree
Hide file tree
Showing 9 changed files with 738 additions and 265 deletions.
62 changes: 60 additions & 2 deletions src/controllers/user.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import {
getAllUsersService,
adminCreateUserService,
adminDeleteUserService,
adminUpdateUserService
adminUpdateUserService,
followUserService,
getUserFollowersService
} from '../services/user.service';
import { isUserExist } from '../services/auth.service';
import Helper from '../services/helper';
Expand Down Expand Up @@ -108,4 +110,60 @@ const adminDeleteUser = async (request, response) => {
}
};

export default { getUsers, adminCreateUser, adminUpdateUser, adminDeleteUser };
/**
* @method followUser
* - user to follow another user
* Route: POST: /users/follow
*
* @param {Object} request request object
* @param {Object} response response object
*
* @returns {Response} response object
*/

const followUser = async (request, response) => {
try {
const userId = request.user.id;
const friendUserId = request.body.userId;

const value = await followUserService(
userId,
parseInt(friendUserId, Number)
);
return Helper.successResponse(response, 200, value);
} catch (error) {
return Helper.failResponse(response, 400, 'user does not exist');
}
};

/**
* @method getFollowers
* - get the list of a user followers
* Route: GET: /users/follow
*
* @param {Object} request request object
* @param {Object} response response object
*
* @returns {Response} response object
*/

const getFollowers = async (request, response, next) => {
try {
const { userId } = request.params;

const value = await getUserFollowersService(parseInt(userId, Number));

return Helper.successResponse(response, 200, value);
} catch (error) {
next(error);
}
};

export default {
getUsers,
adminCreateUser,
adminUpdateUser,
adminDeleteUser,
followUser,
getFollowers
};
46 changes: 46 additions & 0 deletions src/db/migrations/20190708164226-create-follow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Follows', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
required: true,
allowNull: false,
references: {
model: 'Users',
key: 'id',
as: 'userFollowers'
}
},
friendUserId: {
type: Sequelize.INTEGER,
references: {
model: 'Users',
key: 'id',
as: 'Follow'
}
},
isFollowing: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: queryInterface => {
return queryInterface.dropTable('Follows');
}
};
48 changes: 48 additions & 0 deletions src/db/models/follow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
export default (sequelize, DataTypes) => {
const Follow = sequelize.define('Follow', {
userId: {
type: DataTypes.STRING,
allowNull: {
args: false,
msg: 'Please provide a userId'
},
validate: {
isInt: {
args: true,
msg: 'Please provide a user'
}
}
},
friendUserId: {
type: DataTypes.STRING,
allowNull: {
args: false,
msg: 'Please provide a userId'
},
validate: {
isInt: {
args: true,
msg: 'Please provide a user'
}
}
},
isFollowing: {
type: DataTypes.STRING,
allowNull: false,
defaultValue: true
}
});
Follow.associate = models => {
Follow.belongsTo(models.User, {
foreignKey: 'userId',
as: 'userFollowers'
});

Follow.belongsTo(models.User, {
foreignKey: 'friendUserId',
as: 'follower'
});
};

return Follow;
};
11 changes: 11 additions & 0 deletions src/db/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,16 @@ export default (sequelize, DataTypes) => {
}
});

User.associate = models => {
User.hasMany(models.Follow, {
foreignKey: 'userId',
as: 'followers'
});
User.hasMany(models.Follow, {
foreignKey: 'friendUserId',
as: 'followersfriend'
});
};

return User;
};
47 changes: 32 additions & 15 deletions src/routes/v1/user.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ const {
getUsers,
adminCreateUser,
adminUpdateUser,
adminDeleteUser
adminDeleteUser,
followUser,
getFollowers
} = userController;

const { adminCheck, verifyToken, isSuperAdmin } = authorization;
Expand All @@ -17,6 +19,13 @@ const router = express.Router();

router
.get('/', verifyToken, getUsers)
.get(
'/follow/:userId',
authenticationValidator.validator('userId'),
verifyToken,
getFollowers
);
router
.post(
'/create_admin',
verifyToken,
Expand All @@ -26,20 +35,28 @@ router
checkValidationResult,
adminCreateUser
)
.put(
'/update/:userId',
verifyToken,
adminCheck,
validator('userId'),
checkValidationResult,
adminUpdateUser
)
.delete(
'/:userId',
.post(
'/follow',
verifyToken,
adminCheck,
validator('userId'),
checkValidationResult,
adminDeleteUser
authenticationValidator.validator('follow'),
authenticationValidator.checkValidationResult,
followUser
);

router.put(
'/update/:userId',
verifyToken,
adminCheck,
validator('userId'),
checkValidationResult,
adminUpdateUser
);
router.delete(
'/:userId',
verifyToken,
adminCheck,
validator('userId'),
checkValidationResult,
adminDeleteUser
);
export default router;
78 changes: 77 additions & 1 deletion src/services/user.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import getToken from '../helpers/jwt.helper';
import model from '../db/models';
import sendWelcomeEmail from '../helpers/mail.helper';

const { User } = model;
const { User, Follow } = model;

/**
* @description Get all users details without password and confirmEmailCode
Expand Down Expand Up @@ -122,3 +122,79 @@ export const adminDeleteUserService = async userId => {

return response;
};

/**
* @method followUserService
* - follow user follow another user
* - returns true or false
*
* @param {Object} userId user Id of the currently logged in user
* @param {Object} friendUserId user Id of the user to be followed
*
* @returns {Object} user object */

export const followUserService = async (userId, friendUserId) => {
// checks if there is an existing record in the table
const checkIsFollowing = await Follow.findOne({
where: { userId, friendUserId }
});

if (checkIsFollowing) {
const { isFollowing } = checkIsFollowing;

// update the isFollwing column with the opposite boolean
await checkIsFollowing.update({
isFollowing: !isFollowing
});

// if isFollowing is true, it's updated to false
if (isFollowing) {
return 'You have unfollowed this user';
}

// if isFollwing is false, it's updated to true
return 'You have followed this user';
}
await Follow.create({
userId,
friendUserId
});
return 'You have followed this user';
};

/**
* @method followUserService
* - follow user follow another user
* - returns true or false
*
* @param {Object} userId user Id of the currently logged in user
* @param {Object} friendUserId user Id of the user to be followed
*
* @returns {Object} user object */

export const getUserFollowersService = async userId => {
// checks if the user exist in the system
const isUserExist = await User.findOne({ where: { id: userId } });
if (!isUserExist) {
const response = { message: 'Invalid request', status: 400 };
throw response;
}

const followers = await Follow.findAll({
where: { userId },
attributes: ['id'],
include: [
{
model: User,
as: 'follower',
attributes: ['firstName', 'lastName', 'email', 'userName', 'image']
}
]
});
// checks if the user has no followers
if (followers.length === 0) {
return 'User has no follower';
}

return followers;
};
Loading

0 comments on commit e01f26d

Please sign in to comment.