Skip to content

Commit

Permalink
bug(author stats): add endpoint getting the stats of an authors novel
Browse files Browse the repository at this point in the history
- create controller for handling request
- create service for database query

[Fixes #168353582]
  • Loading branch information
OvieMudi committed Sep 6, 2019
1 parent 46f953c commit 8e9f972
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 8 deletions.
2 changes: 1 addition & 1 deletion src/controllers/AuthController.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ const resetPassword = async (request, response) => {

if (passwordExist) {
return responseMessage(response, 403, {
error: 'new password is too similar to previous password'
error: 'you cannot use any of your last 5 passwords'
});
}

Expand Down
23 changes: 21 additions & 2 deletions src/controllers/novelController.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ const {
addNovel, findGenre, findNovel, findAllNovels,
highlightNovelText, getNovelHighlights, findRandomNovels,
findNovelById, bookmarkNovel, getAllBookmark,
updateNovel, removeNovel, toggleReadStatus, findNovelOfTheWeek
updateNovel, removeNovel, toggleReadStatus, findNovelOfTheWeek,
getNovelStats
},
notificationServices: { addNotification }
} = services;
Expand Down Expand Up @@ -404,6 +405,23 @@ const toggleRead = async (request, response) => {
}
};

/**
* @description return the likes and comment activity of the logged in user
* @param {object} request express request object
* @param {object} response express response object
* @returns {json} json
*/
const getAuthorStats = async (request, response) => {
try {
const mostLikedNovels = await getNovelStats(request.params.userId);

return responseMessage(response, 200, { message: mostLikedNovels });
} catch (error) {
log(error.message);
return responseMessage(response, 500, { error: 'an error occurred' });
}
};

export default {
createNovel,
getNovels,
Expand All @@ -417,5 +435,6 @@ export default {
fetchBookmarks,
editNovel,
deleteNovel,
toggleRead
toggleRead,
getAuthorStats
};
25 changes: 24 additions & 1 deletion src/database/seeders/20190807104448-likes.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,28 @@ export const up = queryInterface => queryInterface.bulkInsert('Likes', [{
novelId: '10d42194-2ea2-4c90-aef2-043421952220',
createdAt: new Date(),
updatedAt: new Date()
}], {});
},
{
id: 'd917c842-b16e-4eff-b203-c646a67f4e78',
userId: 'fb94de4d-47ff-4079-89e8-b0186c0a3be8',
novelId: '8bd8c0ec-3b50-4228-bb71-e617c7b8d3b5',
createdAt: new Date(),
updatedAt: new Date()
},
{
id: 'ac8f540a-9867-4745-b857-147076cd5ef4',
userId: 'ce87299b-0dfa-44ed-bb53-45d434647eb2',
novelId: '8bd8c0ec-3b50-4228-bb71-e617c7b8d3b5',
createdAt: new Date(),
updatedAt: new Date()
},
{
id: '23c6743a-e7ba-4283-a20b-bc7d17b3087b',
userId: '11fb0350-5b46-4ace-9a5b-e3b788167915',
novelId: '8bd8c0ec-3b50-4228-bb71-e617c7b8d3b5',
createdAt: new Date(),
updatedAt: new Date()
},

], {});
export const down = queryInterface => queryInterface.bulkDelete('Likes', null, {});
8 changes: 8 additions & 0 deletions src/routes/user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import express from 'express';
import middlewares from '../middlewares';
import userController from '../controllers/userController';
import novelController from '../controllers/novelController';

const {
userValidator: {
Expand All @@ -20,11 +21,16 @@ const {
follow, unfollow, updateUser, deleteUser, getReadingStats
} = userController;

const { getAuthorStats } = novelController;

const user = express.Router();

const USER_URL = '/users';
const PROFILE_URL = '/profiles';

// Route to get most liked novels
user.get(`${PROFILE_URL}/:userId/novels`, validateUUID, verifyToken, authorizeUser(['author', 'admin', 'superadmin']), getAuthorStats);
// Route to get info of novels read by user
user.get(`${PROFILE_URL}/readingstats`, verifyToken, authorizeUser(['author', 'admin', 'superadmin']), getReadingStats);
// Route to get user profile by userId
user.get(`${PROFILE_URL}/:userId`, verifyToken, authorizeUser(['reader', 'author', 'admin', 'superadmin']), profileValidator, getProfile);
Expand All @@ -33,6 +39,8 @@ user.get(`${PROFILE_URL}/:userId`, verifyToken, authorizeUser(['reader', 'author
// Route to edit a user profile
user.patch(`${PROFILE_URL}`, verifyToken, authorizeUser(['reader', 'author', 'admin', 'superadmin']), editProfileValidator, editProfile);
user.post(`${USER_URL}`, verifyToken, authorizeUser(['superadmin']), validateCreateUser, createUser);


// Route to get all users
user.get(`${USER_URL}`, verifyToken, listUsers);
user.get(`${USER_URL}/:userId`, verifyToken, authorizeUser(['admin', 'superadmin']), validateUUID, getUser);
Expand Down
26 changes: 23 additions & 3 deletions src/services/novelService.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import models from '../database/models';
import helpers from '../helpers';

const {
Genre, Novel, Like, User, Highlight, Bookmark, readStats
Genre, Novel, Like, User, Highlight, Bookmark, readStats, Comment
} = models;
const { generateReadTime } = helpers;
const { Op } = Sequelize;
Expand Down Expand Up @@ -173,7 +173,7 @@ const findRandomNovels = async (limit) => {
/**
* @returns {object} json
*/
const findNovelOfTheWeek = async () => {
const findNovelOfTheWeek = async () => { //
const novels = await Novel.findAll({
attributes: {
include: [[Sequelize.fn('COUNT', Sequelize.col('novelId')), 'likescount']],
Expand Down Expand Up @@ -327,6 +327,25 @@ const toggleReadStatus = async (userId, novelId, readStatus) => {
return 'marked as read';
};

/**
* returns novel activity
* @param {object} userId - user id
* @returns {object} object
*/
const getNovelStats = async (userId) => {
const novelLikes = await Novel.findAll({
where: { authorId: userId },
include: [
{ model: Like },
{ model: Comment },
{ model: User, attributes: ['id'] },
],
group: ['Novel.id', 'User.id', 'Likes.id', 'Comments.id']
});

return novelLikes;
};

export default {
findGenre,
findNovel,
Expand All @@ -343,5 +362,6 @@ export default {
bookmarkNovel,
getAllBookmark,
toggleReadStatus,
findNovelOfTheWeek
findNovelOfTheWeek,
getNovelStats
};
2 changes: 1 addition & 1 deletion tests/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@ describe('AUTH', () => {
.end((error, response) => {
expect(response).to.have.status(403);
expect(response.body).to.be.an('object');
expect(response.body.error).to.equal('new password is too similar to previous password');
expect(response.body.error).to.equal('you cannot use any of your last 5 passwords');
done();
});
});
Expand Down
29 changes: 29 additions & 0 deletions tests/novel.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1158,4 +1158,33 @@ describe('GET api/v1/profiles/readingstats', () => {
done();
});
});

// ========================== Novel Stats By Author =================================
describe('GET /profiles/userId/novels', () => {
const authorStatsUrl = `${API_VERSION}/profiles/122a0d86-8b78-4bb8-b28f-8e5f7811c456/novels`;
it('should return the author\'s novels and their activities', (done) => {
chai
.request(server)
.get(authorStatsUrl)
.set('authorization', authToken)
.end((err, res) => {
expect(res).status(200);
done();
});
});
it('should return error 500 on server error', (done) => {
const stub = sinon.stub(Novel, 'findAll');
stub.throws(new Error('error occurred!'));

chai.request(server)
.get(authorStatsUrl)
.set('authorization', authToken)
.end((err, res) => {
expect(res).status(500);
expect(res.body).property('error').include('an error occurred');
stub.restore();
done();
});
});
});
});

0 comments on commit 8e9f972

Please sign in to comment.