Skip to content

Commit

Permalink
Merge 901fa00 into e919633
Browse files Browse the repository at this point in the history
  • Loading branch information
micah-akpan committed Apr 30, 2019
2 parents e919633 + 901fa00 commit 95ece3c
Show file tree
Hide file tree
Showing 10 changed files with 276 additions and 21 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"no-param-reassign": 0,
"comma-dangle": 0,
"camelcase": 0,
"no-console": 0,
"no-console": 1,
"import/prefer-default-export": 0,
"indent": [0, "tab"],
"function-paren-newline": 0,
Expand Down
41 changes: 30 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 51 additions & 4 deletions src/controllers/article.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import sequelize from 'sequelize';
import { Article, Comment, Bookmark, Report, Rating, Sequelize, } from '../models';
import {
Article,
Comment,
Bookmark,
Report,
Rating,
Sequelize,
ArticleReadHistory
} from '../models';
import { slug, userAuthoredThisArticle } from '../utils/article';
import {
responseHandler,
responseFormat,
errorResponseFormat,
omitProps,
sendResponse,
handleDBErrors
handleDBErrors,
addArticleToReadHistory
} from '../utils';
import { findAndCount } from '../utils/query';
import { notify } from '../services/notifyFollowers';
Expand Down Expand Up @@ -244,18 +253,19 @@ export const rateArticle = async (req, res) => {
articleId,
value: userRating
},
attributes: ['id', 'articleId', 'userId', 'value']
});

if (!created) {
const updatedRating = await rating.update({ value: userRating }, { returning: true });
return sendResponse(res, 200, {
responseType: 'success',
data: omitProps(updatedRating.dataValues, ['createdAt', 'updatedAt']),
data: omitProps(updatedRating.get({ plain: true }), ['createdAt', 'updatedAt']),
});
}
return sendResponse(res, 201, {
responseType: 'success',
data: omitProps(rating.dataValues, ['createdAt', 'updatedAt']),
data: rating,
});
} catch (error) {
handleDBErrors(error, { req, Sequelize }, message => sendResponse(res, 500, {
Expand Down Expand Up @@ -327,6 +337,8 @@ export const getAnArticleByID = async (req, res) => {
],
});
if (!article) { return responseHandler(res, 404, { status: 'fail', message: 'Article not found!' }); }
// This article will be added to this user read history
await addArticleToReadHistory(id, req.user.id);
return responseHandler(res, 200, { status: 'success', data: article });
} catch (error) {
return responseHandler(res, 500, { status: 'error', message: 'An internal server error occured!' });
Expand Down Expand Up @@ -355,4 +367,39 @@ export const publishArticle = async (req, res) => {
} catch (error) {
return responseHandler(res, 500, { status: 'error', message: 'An internal server error occured!' });
}
}

/* @description This gets the reading stats of the user
* @function getReadingStats
* @param {Request} req
* @param {Response} res
* @returns {object} Returns response object
*/
export const getReadingStats = async (req, res) => {
try {
const userReadHistory = await ArticleReadHistory.findAll({
where: { userId: req.user.id },
include: [{
model: Article,
as: 'article',
attributes: ['id', 'title', 'slug']
}],
attributes: { exclude: ['createdAt', 'updatedAt'] }
});

const readArticles = userReadHistory.map(history => history.article);

return res.status(200).json({
status: 'success',
data: {
totalArticleReadCount: readArticles.length,
articles: readArticles,
}
});
} catch (error) {
return res.status(500).json({
status: 'error',
message: 'internal server error occurred'
});
}
};
42 changes: 42 additions & 0 deletions src/migrations/20190421143711-create-article-read-history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

export default {
up: (queryInterface, Sequelize) => queryInterface.createTable('article_read_histories', {
id: {
type: Sequelize.UUID,
allowNull: false,
primaryKey: true,
defaultValue: Sequelize.UUIDV4
},
articleId: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: 'articles',
key: 'id',
deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE,
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
},
userId: {
type: Sequelize.UUID,
allowNull: false,
references: {
model: 'users',
key: 'id',
deferrable: Sequelize.Deferrable.INITIALLY_IMMEDIATE
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: queryInterface => queryInterface.dropTable('article_read_histories')
};
46 changes: 46 additions & 0 deletions src/models/article_read_history.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

/**
* @function
* @param {sequelize} sequelize
* @param {DataTypes} DataTypes
* @returns {Model} Returns a database model
*/
export default (sequelize, DataTypes) => {
const ArticleReadHistory = sequelize.define(
'ArticleReadHistory',
{
id: {
type: DataTypes.UUID,
primaryKey: true,
defaultValue: DataTypes.UUIDV4,
},

articleId: {
type: DataTypes.UUID,
allowNull: true,
},

userId: {
type: DataTypes.UUID,
allowNull: true,
},
},
{
tableName: 'article_read_histories'
}
);
ArticleReadHistory.associate = (models) => {
const { Article, User } = models;
ArticleReadHistory.belongsTo(Article, {
foreignKey: 'articleId',
as: 'article',
onDelete: 'CASCADE',
});
ArticleReadHistory.belongsTo(User, {
foreignKey: 'userId',
as: 'user',
onDelete: 'CASCADE',
});
};
return ArticleReadHistory;
};
7 changes: 5 additions & 2 deletions src/routers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
bookmarkArticle,
editArticleTag,
rateArticle,
publishArticle
publishArticle,
getReadingStats
} from '../controllers/article';
import { addComment, editComment, getAllComments, getAComment } from '../controllers/comment';
import checkFields from '../middlewares/auth/loginValidator';
Expand Down Expand Up @@ -78,7 +79,7 @@ router
*/
router
.route('/articles/:id?')
.get(checkQueryParams, getArticleHandler)
.get(checkQueryParams, Auth.authenticateUser, getArticleHandler)
.post(Auth.authenticateUser, articleValidation, addArticle)
.delete(checkParam, Auth.authenticateUser, verifyArticle, isAuthor, deleteArticle)
.put(checkParam, Auth.authenticateUser, articleValidation, verifyArticle, isAuthor, editArticle);
Expand Down Expand Up @@ -183,6 +184,8 @@ router.patch(
publishArticle,
);

router.get('/user-reading-stats', Auth.authenticateUser, getReadingStats);

router.all('*', (req, res) => {
res.status(404).json({
status: 404,
Expand Down
Loading

0 comments on commit 95ece3c

Please sign in to comment.