Skip to content

Commit

Permalink
feature(pagination): add pagination support for article's ratings
Browse files Browse the repository at this point in the history
- add pagination of 10 ratings per page
- add page and/or limit as query params
- add metadata

[Delivers #166790001]
  • Loading branch information
Hervera authored and dmithamo committed Jul 24, 2019
1 parent de8e39c commit ffaad34
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ TWITTER_EMAIL =
CLOUD_NAME=
CLOUD_API_KEY=
CLOUD_API_SECRET=

ALGO_APP_ID=
ALGO_SEARCH_ONLY=
40 changes: 40 additions & 0 deletions controllers/rating.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,46 @@ class Rating {
return res.status(500).json({ error: 'failed to rate article' });
}
}

static async getRating(req, res) {
try {
let page, limit;
if (Object.keys(req.query).length === 0) {
page = 1; limit = 10;
} else if (req.query.limit === undefined) {
({ page } = req.query); limit = 10;
} else ({ page, limit } = req.query);
const { articleSlug } = req.params;
const rating = await ratings.findAll({
where: { articleSlug },
attributes: ['rating'],
offset: ((parseInt(page, 10) - 1) * limit),
limit
});

if (rating.length === 0) {
return res.status(404).json({
error: 'failed to find article ratings'
});
}
const allRatings = await ratings.findAll({ where: { articleSlug } });
return res.status(200).json({
message: 'successfully fetched article ratings',
rating,
metadata: {
currentPage: parseInt(page, 10),
previousPage: parseInt(page, 10) > 1 ? parseInt(page, 10) - 1 : null,
nextPage: Math.ceil(allRatings.length / limit) > page ? parseInt(page, 10) + 1 : null,
totalPages: Math.ceil(allRatings.length / limit),
limit: parseInt(limit, 10)
}
});
} catch (error) {
return res.status(500).json({
error: 'failed to fetch article ratings'
});
}
}
}

export default Rating;
7 changes: 4 additions & 3 deletions migrations/20190710042722-createTableArticles.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ const articleMigration = {
allowNull: true
},
createdAt: {
allowNull: false,
defaultValue: Sequelize.fn('now'),
type: Sequelize.DATE,
default: true
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
defaultValue: Sequelize.fn('now'),
type: Sequelize.DATE,
default: true
}
}),
down: queryInterface => queryInterface.dropTable('articles')
Expand Down
7 changes: 3 additions & 4 deletions models/articles.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SequelizeSlugify from 'sequelize-slugify';

const articleModel = (Sequelize, DataTypes) => {
const articles = Sequelize.define('articles', {
export default (sequelize, DataTypes) => {
const articles = sequelize.define('articles', {
id: {
type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, allowNull: false, primaryKey: true,
},
Expand All @@ -15,11 +15,10 @@ const articleModel = (Sequelize, DataTypes) => {
}, {});
articles.associate = (models) => {
articles.belongsTo(models.users, { as: 'author', foreignKey: 'authorId', onDelete: 'CASCADE' });
articles.hasMany(models.ratings, { foreignKey: 'articleSlug' });
};
SequelizeSlugify.slugifyModel(articles, {
source: ['title'], slugOptions: { lower: true }, overwrite: true, column: 'slug'
});
return articles;
};

export default articleModel;
2 changes: 2 additions & 0 deletions models/ratings.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@ export default (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
allowNull: false
},

createdAt: {
allowNull: false,
type: DataTypes.DATE
},

updatedAt: {
allowNull: false,
type: DataTypes.DATE
Expand Down
5 changes: 3 additions & 2 deletions models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default (sequelize, DataTypes) => {
allowNull: false,
unique: true
},

email: {
type: DataTypes.STRING,
allowNull: false,
Expand All @@ -22,7 +23,6 @@ export default (sequelize, DataTypes) => {
isEmail: true
}
},
provider: { type: DataTypes.STRING },
password: {
type: DataTypes.STRING,
allowNull: false,
Expand Down Expand Up @@ -71,8 +71,9 @@ export default (sequelize, DataTypes) => {
},
}, {});
users.associate = (models) => {
users.hasMany(models.articles, { foreignKey: 'authorId', allowNull: false });
users.hasMany(models.articles, { as: 'author', foreignKey: 'authorId' });
users.hasMany(models.comments, { foreignKey: 'authorId', allowNull: false });
users.hasMany(models.ratings, { foreignKey: 'userId' });
};
return users;
};
46 changes: 24 additions & 22 deletions package-lock.json

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

1 change: 1 addition & 0 deletions routes/api/ratings.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ const router = express.Router();
*/

router.post('/article/:articleSlug/rate', checkToken, ratingValidations.validateRating, ratingController.rateArticle);
router.get('/article/:articleSlug/rating', ratingController.getRating);

export default router;
Empty file removed routes/api/routes.js
Empty file.
14 changes: 14 additions & 0 deletions routes/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,20 @@ router.get('/reset-password/:token', resetPasswordController.getToken);
*/
router.put('/password', Validations.validatePasswordOnReset, resetPasswordController.resetPassword);
router.post('/users/login', Validations.validateSiginUser, signin);
router.post('/users/login', Validations.validateSiginUser, signin);
// facebook router
router.get('/auth/facebook', passport.authenticate('facebook', { scope: ['email'] }));

router.get('/auth/facebook/callback', passport.authenticate('facebook'), socialAuth.userFacebookGoogle);
// google router
router.get('/auth/google', passport.authenticate('google', { scope: ['email', 'profile'] }));

router.get('/auth/google/callback', passport.authenticate('google'), socialAuth.userFacebookGoogle);
// twitter router
router.get('/auth/twitter', passport.authenticate('twitter', { scope: ['email', 'profile'] }));

router.get('/auth/twitter/callback', passport.authenticate('twitter'), socialAuth.userTwitter);

router.get('/users/verify/:token', verifyUser);
router.post('/users/signout', checkToken, signoutUser);
/**
Expand Down
1 change: 0 additions & 1 deletion seeders/20190707095336-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,4 @@ export const up = (queryInterface, Sequelize) => queryInterface.bulkInsert('user
],
{}
)

export const down = (queryInterface, Sequelize) => queryInterface.bulkDelete('users', null, {});
54 changes: 54 additions & 0 deletions tests/rating.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,58 @@ describe('Testing if user can rate and see a rating of articles', () => {
done();
});
});

it('should fetch all ratings of a given article', (done) => {
chai.request(app)
.get('/api/article/the-basics-of-javaa/rating')
.end((error, res) => {
const { status, body } = res;
expect(status).to.equal(200);
expect(body).to.have.property('rating');
expect(body.message).to.have.equals('successfully fetched article ratings');
done();
});
});

it('should not fetch ratings of an article which is not registered/published', (done) => {
chai.request(app)
.get('/api/article/the-basics-of-ja/rating')
.end((error, res) => {
const { status, body } = res;
expect(status).to.equal(404);
expect(body).to.have.property('error');
expect(body.error).to.have.equals('failed to find article ratings');
done();
});
});

it('should return the article ratings on a the first page', (done) => {
chai.request(app)
.get('/api/article/the-basics-of-javaa/rating?page=1')
.end((err, res) => {
if (err) done(err);
const { status, body } = res;
expect(status).to.equal(200);
expect(typeof body).to.be.equal('object');
expect(body).to.have.property('rating');
expect(Object.prototype.toString.call(res.body.rating)).to.be.equal('[object Array]');
expect(body).to.have.property('metadata');
done();
});
});

it('should return all article ratings on the first page with specified limit', (done) => {
chai.request(app)
.get('/api/article/the-basics-of-javaa/rating?page=1&limit=1')
.end((err, res) => {
if (err) done(err);
const { status, body } = res;
expect(status).to.equal(200);
expect(typeof body).to.be.equal('object');
expect(body).to.have.property('rating');
expect(Object.prototype.toString.call(res.body.rating)).to.be.equal('[object Array]');
expect(body).to.have.property('metadata');
done();
});
});
});

0 comments on commit ffaad34

Please sign in to comment.