Skip to content

Commit

Permalink
feature(rate article): add user should be able to give a rating to an…
Browse files Browse the repository at this point in the history
… article [Finishes 167313404]
  • Loading branch information
minega25 committed Aug 16, 2019
1 parent 39767d4 commit 99fefe7
Show file tree
Hide file tree
Showing 16 changed files with 672 additions and 100 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ TWITTER_SECRET='ggug'
CLOUDINARY_API_KEY=xxxxxxxxxxxxxxxxx
CLOUDINARY_SECRET_KEY=xxxxxxxxxxxxxxxx
CLOUDINARY_NAME=xxxxxxxxxxxxx
TWITTER_CALLBACK_URL='ghgfh'
132 changes: 66 additions & 66 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"scripts": {
"start": "npm run undoseeds && npm run migration && npm run seeds && babel-node ./src/app.js",
"seeds": "babel-node node_modules/.bin/sequelize db:seed:all --seeders-path src/seeders",
"undoseeds": "babel-node node_modules/.bin/sequelize db:seed:undo",
"undoseeds": "babel-node node_modules/.bin/sequelize db:seed:undo:all",
"migration": "babel-node node_modules/.bin/sequelize db:migrate",
"undomigration": "export NODE_ENV=test && npm run undoseeds && babel-node node_modules/.bin/sequelize db:migrate:undo:all",
"undomigration": "babel-node node_modules/.bin/sequelize db:migrate:undo:all",
"runmigrations": "npm run undoseeds && npm run migration && npm run seeds",
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"test": "export NODE_ENV=test && npm run undomigration && npm run migration && npm run seeds && nyc --reporter=html --reporter=text mocha ./test --no-timeout --exit --require @babel/register",
Expand Down
129 changes: 129 additions & 0 deletions src/controllers/rating.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import db from '../models/index';
import RateService from '../services/rate.service';
/**
*
*
* @class rateController
*/
class rateController {
/**
*
*
* @static
* @param {*} req
* @param {*} res
* @returns {Object} return rating information to user
* @memberof UserController
*/
static async setArticleRating(req, res) {
try {
// Initialize rating data
const userEmail = req.auth.email;
const { rate } = req.body;
const { articleSlug } = req.params;
const rateSchema = { userEmail, articleSlug, rate };

// check if user is trying to rate his/her own article
const article = await db.Article.findOne({
where: { slug: articleSlug }
});
const user = await db.user.findOne({
where: { id: article.authorId }
});
if (user.email === userEmail) {
return res.status(400).send({
status: 400,
message: 'You cannot rate your own article'
});
}

// create rating
const createdRate = await RateService.create(rateSchema);
return res.status(200).send({
status: 200,
message: 'Thank you for rating this article',
data: createdRate
});
} catch (error) {
return res.status(404).send({
status: 404,
message: error.message
});
}
}

/**
*
*
* @static
* @param {*} req
* @param {*} res
* @returns {Object} return rating information to user
* @memberof UserController
*/
static async updateArticleRating(req, res) {
try {
// Initialize rating data
const userEmail = req.auth.email;
const { rate } = req.body;
const { articleSlug } = req.params;

// update article rate
const updatedRate = await RateService.update(articleSlug, userEmail, rate);
if (!updatedRate) {
return res.status(400).send({
status: 400,
error: `Rating for article with Slug: ${articleSlug} not found`
});
}
return res.status(200).send({
status: 200,
message: 'Thank you for rating this article',
newRating: updatedRate
});
} catch (error) {
return res.status(404).send({
status: 404,
message: error.message
});
}
}

/**
*
*
* @static
* @param {*} req
* @param {*} res
* @returns {Object} return rating information to user
* @memberof rateController
*/
static async getArticleRating(req, res) {
try {
// Initialize rating data
const userEmail = req.auth.email;
const { articleSlug } = req.params;

// find particular rating
const userRate = await RateService.findOne(articleSlug, userEmail);
if (!userRate) {
return res.status(400).send({
status: 400,
error: `Rating for article with slug ${articleSlug} not found`,
});
}
return res.status(200).send({
status: 200,
message: `Rating for article with slug ${articleSlug} found`,
data: userRate
});
} catch (error) {
return res.status(404).send({
status: 404,
message: error.message
});
}
}
}

export default rateController;
21 changes: 21 additions & 0 deletions src/middlewares/validators/rate.validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Joi from 'joi';
import Util from '../../helpers/util';

const util = new Util();

export default (req, res, next) => {
const { rate } = req.body;

const schema = {
rate: Joi.number().integer().min(1).max(5),
};
const { error } = Joi.validate({
rate
}, schema);

if (!error) return next();
const errorMessageFromJoi = error.details[0].message;
const removeEscapeCharacters = errorMessageFromJoi.replace(/['"]+/g, '');
util.setError(400, removeEscapeCharacters);
return util.send(res);
};
8 changes: 4 additions & 4 deletions src/migrations/20190805142252-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
},
email: {
allowNull: false,
unique:true,
unique: true,
type: Sequelize.STRING
},
password: {
Expand All @@ -28,7 +28,7 @@ module.exports = {
username: {
allowNull: false,
type: Sequelize.STRING,
unique:true
unique: true
},
bio: {
allowNull: true,
Expand All @@ -41,7 +41,7 @@ module.exports = {
role: {
allowNull: false,
type: Sequelize.STRING,
defaultValue:'normal'
defaultValue: 'normal'
},
verified: {
allowNull: false,
Expand All @@ -61,4 +61,4 @@ module.exports = {
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('users');
}
};
};
47 changes: 47 additions & 0 deletions src/migrations/20190814093719-create-rate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Rates', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userEmail: {
type: Sequelize.STRING,
references: {
model: 'users',
key: 'email',
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
},
articleSlug: {
type: Sequelize.STRING,
references: {
model: 'Articles',
key: 'slug',
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
},
rate: {
type: Sequelize.ENUM({
values: [1, 2, 3, 4, 5]
})
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Rates');
}
};
24 changes: 12 additions & 12 deletions src/models/article.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
module.exports = (sequelize, DataTypes) => {
const Article = sequelize.define('Article', {
slug: {type: DataTypes.STRING, allowNull: false},
title: {type: DataTypes.STRING, allowNull: false},
description: {type: DataTypes.TEXT, allowNull: false},
body: {type: DataTypes.TEXT, allowNull: false},
taglist: {type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true},
favorited: {type: DataTypes.BOOLEAN, allowNull: true, defaultValue: false},
favoritedcount: {type: DataTypes.INTEGER, defaultValue: 0},
flagged: {type: DataTypes.BOOLEAN, defaultValue: false},
authorId: {type: DataTypes.INTEGER, allowNull: false},
images: {type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true},
slug: { type: DataTypes.STRING, allowNull: false },
title: { type: DataTypes.STRING, allowNull: false },
description: { type: DataTypes.TEXT, allowNull: false },
body: { type: DataTypes.TEXT, allowNull: false },
taglist: { type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true },
favorited: { type: DataTypes.BOOLEAN, allowNull: true, defaultValue: false },
favoritedcount: { type: DataTypes.INTEGER, defaultValue: 0 },
flagged: { type: DataTypes.BOOLEAN, defaultValue: false },
authorId: { type: DataTypes.INTEGER, allowNull: false },
images: { type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true },
views: DataTypes.INTEGER
}, {});
Article.associate = function(models) {
Article.associate = function (models) {
Article.belongsTo(models.user, {
as:'author',
as: 'author',
targetKey: 'id',
onUpdate: 'CASCADE',
onDelete: 'CASCADE'
Expand Down
35 changes: 35 additions & 0 deletions src/models/rate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const Rate = sequelize.define('Rate', {
userEmail: {
type: DataTypes.STRING,
references: {
model: 'users',
key: 'email',
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
},
articleSlug: {
type: DataTypes.STRING,
references: {
model: 'Articles',
key: 'slug',
},
onDelete: 'CASCADE',
onUpdate: 'CASCADE'
},
rate: DataTypes.ENUM({
values: [1, 2, 3, 4, 5]
})
}, {});
Rate.associate = function (models) {
Rate.belongsTo(models.user, {
foreignKey: 'userEmail',
});
Rate.belongsTo(models.Article, {
foreignKey: 'articleSlug',
});
};
return Rate;
};
34 changes: 18 additions & 16 deletions src/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@ module.exports = (sequelize, DataTypes) => {
username: DataTypes.STRING,
image: DataTypes.STRING,
bio: DataTypes.STRING,
role: {type:DataTypes.STRING, defaultValue: 'normal'},
verified: {type: DataTypes.BOOLEAN, defaultValue: false},
role: { type: DataTypes.STRING, defaultValue: 'normal' },
verified: { type: DataTypes.BOOLEAN, defaultValue: false },
role: { type: DataTypes.STRING, defaultValue: 'normal' },
verified: { type: DataTypes.BOOLEAN, defaultValue: false },
}, {});
user.associate = ({
Follow,Article
}) => {
user.hasMany(Follow, {
foreignKey: 'followerId',
as: 'followerDetails'
});
user.hasMany(Article, {
as:'author',
foreignKey:'authorId'
})
};

user.associate = ({
Follow, Article

}) => {
user.hasMany(Follow, {
foreignKey: 'followerId',
as: 'followerDetails'
});
user.hasMany(Article, {
as: 'author',
foreignKey: 'authorId'
})
};
return user;
};
2 changes: 2 additions & 0 deletions src/routes/api/index.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import oauth from './oauth/oauth.routes';
import user from './user/user.route';
import article from './article/article.routes';
import profile from './profile/profile.route';
import rate from './rate/rate.route';

const router = express.Router();
router.use('/images', express.static(path.join(__dirname, 'images')));
Expand All @@ -15,6 +16,7 @@ router.use(oauth);
router.use('/profile', profile);
router.use('/users', user);
router.use('/', article);
router.use('/rate', rate);
router.use('/api-docs', swaggerui.serve, swaggerui.setup(swaggerSpecification));


Expand Down
Loading

0 comments on commit 99fefe7

Please sign in to comment.