Skip to content

Commit

Permalink
[ft-165412937] add reading stats
Browse files Browse the repository at this point in the history
  • Loading branch information
luc-tuyishime committed Jun 12, 2019
1 parent 52f306e commit 1d47424
Show file tree
Hide file tree
Showing 14 changed files with 733 additions and 2 deletions.
46 changes: 46 additions & 0 deletions src/controllers/ReadingStatController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { getAllRatings, createRatings } from '../queries';
import status from '../config/status';

/**
* A class to handle Reading Stats for users
*/
export default class ReadingStat {
/**
* Save reading stats
* @param {object} req
* @param {object} res
* @returns {object} stats object
*/
static async create(req, res) {
const userId = req.user.id;
const { slug } = req.params;

const saveStat = await createRatings({
userId,
articleSlug: slug
});

return res.status(status.OK).json({
saveStat
});
}

/**
* Get reading stats
* @param {Object} req express request
* @param {Object} res express response
* @returns {Array} user reading statistics
*/
static async getAll(req, res) {
const userId = req.user.id;

const readingStats = await getAllRatings({
userId
});

return res.status(status.OK).json({
message: 'Reading Stats fetched',
readingStats
});
}
}
34 changes: 34 additions & 0 deletions src/migrations/20190606145424-create-reading-stat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('ReadingStats', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER,
allowNull: true,
references: {
model: 'Users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'SET NULL'
},
articleSlug: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: (queryInterface, Sequelize) => queryInterface.dropTable('ReadingStats')
};
47 changes: 47 additions & 0 deletions src/models/readingstat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module.exports = (sequelize, DataTypes) => {
const ReadingStat = sequelize.define(
'ReadingStat',
{
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
references: {
model: 'Users',
key: 'id'
},
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
},
articleSlug: {
type: DataTypes.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: DataTypes.DATE
},
updatedAt: {
allowNull: false,
type: DataTypes.DATE
}
},
{}
);
ReadingStat.associate = (models) => {
ReadingStat.belongsTo(models.User, {
foreignKey: 'userId',
as: 'user'
});
ReadingStat.belongsTo(models.Article, {
foreignKey: 'articleSlug',
as: 'article'
});
};
return ReadingStat;
};
7 changes: 5 additions & 2 deletions src/queries/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import * as Tag from './tags';
import * as Notification from './notifications';
import * as Chat from './chats';
import * as Gallery from './gallery';
import { getAllRatings, createRatings } from './readingStats';

export {
User, Token, Chat, Notification
User, Token, Chat, Notification, Gallery
};

export { Article, Tag, Gallery };
export {
Tag, getAllRatings, Article, createRatings
};
11 changes: 11 additions & 0 deletions src/queries/readingStats/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import db from '../../models';

/**
* @param {object} data inputs data to be saved in db
* @returns {object} Object representing the response returned
*/
export default async (data) => {
let stats = [];
stats = await db.ReadingStat.create(data, { logging: false });
return stats;
};
21 changes: 21 additions & 0 deletions src/queries/readingStats/getAll.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import db from '../../models';

/**
* Get user stats
* @param {object} condition condition for query
* @returns {object} Object representing the response returned
*/
export default async (condition = {}) => db.ReadingStat.findAndCountAll({
where: condition,
logging: false,
include: [
{
model: db.User,
as: 'user',
attributes: {
exclude: ['createdAt', 'updatedAt']
}
}
],
order: [['createdAt', 'DESC']]
});
4 changes: 4 additions & 0 deletions src/queries/readingStats/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import getAllRatings from './getAll';
import createRatings from './create';

export { getAllRatings, createRatings };
2 changes: 2 additions & 0 deletions src/routes/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import permissions from './permissions';
import highlights from './highlights';
import notifications from './notifications';
import rating from './rating';
import readingStats from './readingStats';

const router = express.Router();

Expand All @@ -28,5 +29,6 @@ router.use('/permissions', permissions);
router.use(highlights);
router.use('/notifications', notifications);
router.use(rating);
router.use('/user', readingStats);

export default router;
18 changes: 18 additions & 0 deletions src/routes/api/readingStats.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Router } from 'express';
import ReadingStat from '../../controllers/ReadingStatController';
import verifyToken from '../../middlewares/verifyToken';
import asyncHandler from '../../middlewares/asyncHandler';
import checkArticleBySlug from '../../middlewares/checkArticleBySlug';

const statsRouter = Router();

statsRouter.post(
'/profile/:slug/stats',
verifyToken,
checkArticleBySlug,
asyncHandler(ReadingStat.create)
);

statsRouter.get('/profile/stats', verifyToken, asyncHandler(ReadingStat.getAll));

export default statsRouter;
1 change: 1 addition & 0 deletions src/tests/routes/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './readingStats.test';
import './comments.test';
import './404.test';
import './auth.test';
Expand Down
66 changes: 66 additions & 0 deletions src/tests/routes/readingStats.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable no-undef */
/* eslint-disable import/no-extraneous-dependencies */
import chai from 'chai';
import chaiHttp from 'chai-http';
import jwt from 'jsonwebtoken';
import * as Factory from '../../helpers/factory';
import status from '../../config/status';
import db from '../../models';
import app from '../../app';

chai.use(chaiHttp);
chai.should();
let accessToken;
let createdUser = {};
let createdArticle = {};

const newUser = Factory.user.build();
const newArticle = Factory.article.build();

delete newUser.id;
delete newArticle.id;

describe('Reading Stats', () => {
before(async () => {
try {
await db.User.destroy({
truncate: true,
cascade: true,
logging: false
});

createdUser = (await db.User.create(newUser, { logging: false })).dataValues;
accessToken = jwt.sign(
{ id: createdUser.id, role: createdUser.role, permissions: createdUser.permissions },
process.env.SECRET_KEY,
{ expiresIn: '1d' }
);
newArticle.userId = createdUser.id;
createdArticle = (await db.Article.create(newArticle, { logging: false })).dataValues;
} catch (err) {
throw err;
}
});

it('Should let the user save a reading stats', (done) => {
chai
.request(app)
.post(`/api/v1/user/profile/${createdArticle.slug}/stats`)
.set('access-token', accessToken)
.end((err, res) => {
res.should.have.status(status.OK);
done();
});
});

it('Should let the user get reading stats', (done) => {
chai
.request(app)
.get('/api/v1/user/profile/stats')
.set('access-token', accessToken)
.end((err, res) => {
res.should.have.status(status.OK);
done();
});
});
});
Loading

0 comments on commit 1d47424

Please sign in to comment.