Skip to content

Commit

Permalink
Merge c6adf51 into 0524f71
Browse files Browse the repository at this point in the history
  • Loading branch information
adaezeodurukwe committed Mar 6, 2019
2 parents 0524f71 + c6adf51 commit 346e572
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 10 deletions.
3 changes: 3 additions & 0 deletions server/controllers/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ class ArticleController {
'totalClaps',
'createdAt',
'updatedAt'
],
order: [
['id', 'ASC'],
]
};

Expand Down
45 changes: 45 additions & 0 deletions server/controllers/articleClap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import dotenv from 'dotenv';
import db from '../database/models';
import { response } from '../utils';

const { Article, ArticleClap } = db;
dotenv.config();

/**
* @description Class to implement clap and unclap
*/
export default class ArticleClapToggle {
/**
* @description claps and retrieves claap from articles
* @param {object} req
* @param {object} res
* @returns {object} response object
*/
static async clapToggle(req, res) {
const { id } = req.user;
const { slug } = req.params;

const getArticle = await Article.findOne({ where: { slug } });

if (!getArticle) response(res).notFound({ message: 'article not found' });

else if (getArticle.userId === id) response(res).forbidden({ message: 'you cannot rate your own article' });

else {
const clap = await ArticleClap.findOrCreate({
where: { userId: id, articleId: getArticle.id },
defaults: { clap: true }
});

if (clap[1] === true) response(res).created({ message: 'you just clapped for this article' });

else {
await ArticleClap.destroy({
where: { userId: id, articleId: getArticle.id }
});

response(res).success({ message: 'you just retrieved your clap' });
}
}
}
}
3 changes: 2 additions & 1 deletion server/controllers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import Users from './user';
import ArticleController from './article';
import Comment from './articleComment';
import Follow from './follow';
import ArticleClap from './articleClap';

export {
Users, ArticleController, Follow, Comment
Users, ArticleController, Comment, Follow, ArticleClap
};
30 changes: 30 additions & 0 deletions server/database/migrations/20190305175826-create-article-clap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* eslint no-unused-vars: "off" */

module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('ArticleClaps', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
userId: {
type: Sequelize.INTEGER
},
articleId: {
type: Sequelize.INTEGER
},
clap: {
type: Sequelize.BOOLEAN
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: (queryInterface, Sequelize) => queryInterface.dropTable('ArticleClaps')
};
5 changes: 5 additions & 0 deletions server/database/models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ export default (sequelize, DataTypes) => {
Article.hasMany(models.ArticleComment, {
foreignKey: 'id',
});

Article.hasMany(models.ArticleClap, {
foreignKey: 'articleId',
as: 'claps'
});
};
return Article;
};
30 changes: 30 additions & 0 deletions server/database/models/articleclap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default (sequelize, DataTypes) => {
const ArticleClap = sequelize.define('ArticleClap', {
userId: {
type: DataTypes.INTEGER,
allowNull: false
},
articleId: {
type: DataTypes.INTEGER,
allowNull: false
},
clap: {
type: DataTypes.BOOLEAN,
}
}, {});
ArticleClap.associate = (models) => {
// associations can be defined here
const { User, Article } = models;

ArticleClap.belongsTo(User, {
foreignKey: 'userId',
onDelete: 'CASCADE'
});

ArticleClap.belongsTo(Article, {
foreignKey: 'articleId',
onDelete: 'CASCADE'
});
};
return ArticleClap;
};
6 changes: 5 additions & 1 deletion server/database/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default (sequelize, DataTypes) => {
}
}, {});
User.associate = (models) => {
const { Follower } = models;
const { Follower, ArticleClap } = models;
User.belongsToMany(User, {
through: Follower,
foreignKey: 'userId',
Expand All @@ -60,6 +60,10 @@ export default (sequelize, DataTypes) => {
as: 'followers'
});

User.hasMany(ArticleClap, {
foreignKey: 'userId',
});

// Relations for articles.
User.hasMany(models.Article, {
foreignKey: 'id',
Expand Down
10 changes: 10 additions & 0 deletions server/routes/articleClap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import express from 'express';
import { ArticleClap } from '../controllers';
import { AuthenticateUser } from '../middlewares';

const articleClapRoute = express.Router();

// Article toggle route
articleClapRoute.post('/articles/:slug/clapToggle', AuthenticateUser.verifyUser, ArticleClap.clapToggle);

export default articleClapRoute;
3 changes: 3 additions & 0 deletions server/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import express from 'express';
import articleRoutes from './articles';
import authRoute from './users';
import profileRoutes from './profile';
import articleClapRoute from './articleClap';

const router = express.Router();

router.use(articleRoutes);
router.use(authRoute);
router.use(profileRoutes);
router.use(articleClapRoute);


export default router;
129 changes: 129 additions & 0 deletions server/test/articleClap.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import chaiHttp from 'chai-http';
import chai, { expect } from 'chai';
import dotenv from 'dotenv';
import app from '../app';

dotenv.config();


const articleURL = '/api/articles';
const signupURL = '/api/users';
let userToken;
let secondUserToken;
let anArticleSlug = '';
const data = {
firstUser: {
firstname: 'John',
lastname: 'Doe',
email: 'obiora44@gmail.com',
username: 'john466',
password: '1234567122'
},
secondUser: {
firstname: 'adaeze',
lastname: 'Doe',
email: 'adaezeboo@gmail.com',
username: 'john500',
password: '1234536719'
},
createArticle: {
title: 'some title',
description: 'some weird talk',
body: 'article body'
}
};

chai.use(chaiHttp);

describe('Clap endpoint test', () => {
// SignUp first user
before('should create user', (done) => {
chai
.request(app)
.post(signupURL)
.send(data.firstUser)
.end((err, res) => {
userToken = res.body.user.token;
done();
});
});
// SignUp second user
before('should create user', (done) => {
chai
.request(app)
.post(signupURL)
.send(data.secondUser)
.end((err, res) => {
secondUserToken = res.body.user.token;
done();
});
});
// Create article

it('should create an article', (done) => {
chai
.request(app)
.post(articleURL)
.set('Authorization', `Bearer ${userToken}`)
.send(data.createArticle)
.end((err, res) => {
anArticleSlug = res.body.article.slug;
done();
});
});
// Test for article not found
it('should return 404 for article not found', (done) => {
chai
.request(app)
.post(`${articleURL}/somenewarticleSlug/clapToggle`)
.set('Authorization', `Bearer ${secondUserToken}`)
.end((err, res) => {
expect(res.status).to.equal(404);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('article not found');
done();
});
});

// Test article clap
it('should like an article and return 200', (done) => {
chai
.request(app)
.post(`${articleURL}/${anArticleSlug}/clapToggle`)
.set('Authorization', `Bearer ${secondUserToken}`)
.end((err, res) => {
expect(res.status).to.equal(201);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('you just clapped for this article');
done();
});
});

// Test for article belonging to the user
it('user should be unable to clap for own article', (done) => {
chai
.request(app)
.post(`${articleURL}/${anArticleSlug}/clapToggle`)
.set('Authorization', `Bearer ${userToken}`)
.end((err, res) => {
expect(res.status).to.equal(403);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('you cannot rate your own article');
done();
});
});

// Test clap retrieval
it('should retrieve clap from an article and return 200', (done) => {
chai
.request(app)
.post(`${articleURL}/${anArticleSlug}/clapToggle`)
.set('Authorization', `Bearer ${secondUserToken}`)
.end((err, res) => {
expect(res.status).to.equal(200);
expect(res.body.message).to.be.a('string');
expect(res.body.message).to.equal('you just retrieved your clap');
done();
});
});
});
2 changes: 1 addition & 1 deletion server/test/articles.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ describe('GET single article /api/articles/:slug', () => {
it('should return 200 if article exists', (done) => {
chai
.request(app)
.get('/api/articles/this-is-an-article-3')
.get('/api/articles/the-second-article-3')
.end((err, res) => {
const { id } = res.body.messages;
expect(res.status).to.be.equal(200);
Expand Down
8 changes: 3 additions & 5 deletions server/test/comment.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,14 @@ describe('POST comment /api/articles/:id/comment', () => {

it('should create a new comment', (done) => {
chai.request(app)
.post('/api/articles/this-is-an-article-1/comment')
.post('/api/articles/this-is-an-article-2/comment')
.set('authorization', `Bearer ${userToken}`)
.send({
comment: 'This is a random comment'
})
.end((err, res) => {
const { articleId, comment } = res.body.userComment;
const { comment } = res.body.userComment;
expect(res.status).to.be.equal(201);
expect(articleId).to.be.equal(1);
expect(comment).to.be.equal('This is a random comment');
done(err);
});
Expand Down Expand Up @@ -98,9 +97,8 @@ describe('PATCH update comment', () => {
comment: 'This is an updated random comment'
})
.end((err, res) => {
const { articleId, comment } = res.body.userComment;
const { comment } = res.body.userComment;
expect(res.status).to.be.equal(200);
expect(articleId).to.be.equal(1);
expect(comment).to.be.equal('This is an updated random comment');
done(err);
});
Expand Down
1 change: 1 addition & 0 deletions server/test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ import './articles.spec';
import './comment.spec';
import './follow.spec';
import './profile.spec';
import './articleClap.spec';
3 changes: 1 addition & 2 deletions server/test/profile.spec.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import chaiHttp from 'chai-http';
import chai, { expect } from 'chai';
import dotenv from 'dotenv';
// import HelperUtils from '../utils';
import app from '../app';

dotenv.config();
Expand All @@ -19,7 +18,7 @@ before('It should return a 201 and create a new user', (done) => {
const data = {
firstname: 'John',
lastname: 'Doe',
email: 'obiora@gmail.com',
email: 'obiora200@gmail.com',
username: 'john46',
password: '12345671'
};
Expand Down

0 comments on commit 346e572

Please sign in to comment.