Skip to content

Commit

Permalink
Merge a6cd83c into b105a35
Browse files Browse the repository at this point in the history
  • Loading branch information
chikeozulumba committed Apr 26, 2019
2 parents b105a35 + a6cd83c commit 1be1a42
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 24 deletions.
56 changes: 44 additions & 12 deletions src/controllers/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { findAndCount } from '../utils/query';
export const addArticle = async (req, res) => {
const { body } = req;
const { id: userId } = req.user;
delete body.isDeletedByAuthor;
try {
const article = await Article.create({ userId, slug: slug(body.title), ...body });
return responseHandler(res, 201, {
Expand Down Expand Up @@ -49,9 +50,11 @@ export const addArticle = async (req, res) => {
*/
export const editArticle = async (req, res) => {
const success = 1;
const { body } = req;
delete body.isDeletedByAuthor;
try {
const { id } = req.params;
const article = await Article.update(req.body, { where: { id }, returning: true });
const article = await Article.update(body, { where: { id }, returning: true });
if (success === article[0]) {
const [, [updatedArticle]] = article;
return responseHandler(res, 202, {
Expand Down Expand Up @@ -109,9 +112,12 @@ export const deleteArticle = async (req, res) => {
const { id } = req.params;
try {
const deleted = 1;
const destroyArticle = await Article.destroy({ where: { id } });
const destroyArticle = await Article.update(
{ isDeletedByAuthor: true },
{ where: { id }, },
);
if (destroyArticle >= deleted) {
return responseHandler(res, 202, {
return responseHandler(res, 200, {
status: 'success',
message: 'Your article has been removed.',
});
Expand Down Expand Up @@ -268,7 +274,7 @@ export const getAllArticles = async (req, res) => {
const paginate = { limit, offset, subQuery: false };

const articles = await Article.findAll({
where: { published: true },
where: { published: true, isDeletedByAuthor: false, },
order: [['createdAt', 'ASC']],
group: ['Article.id', 'comments.id'],
...paginate,
Expand All @@ -292,25 +298,51 @@ export const getAllArticles = async (req, res) => {
* @description This is the method for deleting an article
* @param {object} req The request object
* @param {object} res The response object
* @returns {int} Returns true after deleting an article
* @returns {boolean} Returns true after deleting an article
* @returns {object} Returns response object
*/
export const getAnArticleByID = async (req, res) => {
const { id } = req.params;
try {
const article = await Article.findOne({
where: { id, published: true },
where: { id, published: true, isDeletedByAuthor: false, },
group: ['Article.id'],
include: [{
model: Comment,
as: 'comments',
limit: 10,
offset: 0,
}]
include: [
{
model: Comment,
as: 'comments',
limit: 10,
offset: 0,
},
],
});
if (!article) { return responseHandler(res, 404, { status: 'fail', message: 'Article not found!' }); }
return responseHandler(res, 200, { status: 'success', data: article });
} catch (error) {
return responseHandler(res, 500, { status: 'error', message: 'An internal server error occured!' });
}
};

/**
* @name publishArticle
* @description This is the method for deleting an article
* @param {object} req The request object
* @param {object} res The response object
* @returns {boolean} Returns true after deleting an article
* @returns {object} Returns response object
*/
export const publishArticle = async (req, res) => {
try {
const { articleId: id, } = req.params;
const article = await Article.update({ published: true, }, { where: { id, }, returning: true });
if (!article) { return responseHandler(res, 404, { status: 'fail', message: 'Article not found!' }); }
const [, [publishedArticle]] = article;
return responseHandler(res, 200, {
status: 'success',
message: 'Your article has been published!',
data: publishedArticle,
});
} catch (error) {
return responseHandler(res, 500, { status: 'error', message: 'An internal server error occured!' });
}
};
6 changes: 4 additions & 2 deletions src/middlewares/articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@ export const articleValidation = async (req, res, next) => {
* @returns {function} Calls next function/action
*/
export const verifyArticle = async (req, res, next) => {
const { id } = req.params;
const article = await findById(Article, id);
let { id, articleId } = req.params;
id = !id ? articleId : id;
articleId = !articleId ? id : articleId;
const article = await findById(Article, { id, articleId, }, { isDeletedByAuthor: false });
if (!article) { return responseHandler(res, 404, { status: 'fail', message: 'Article not found!', }); }
const { dataValues: { userId: authorId } } = article;
req.authorId = authorId;
Expand Down
4 changes: 4 additions & 0 deletions src/migrations/20190331141021-create-article.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export default {
type: Sequelize.INTEGER,
defaultValue: 1,
},
isDeletedByAuthor: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
Expand Down
18 changes: 13 additions & 5 deletions src/models/article.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,20 +61,28 @@ export default (sequelize, DataTypes) => {
type: DataTypes.INTEGER,
defaultValue: 1,
},
isDeletedByAuthor: {
type: DataTypes.BOOLEAN,
defaultValue: false,
},
},
{
tableName: 'articles',
hooks: {
beforeCreate(article) {
const totalReadTime = computeArticleReadingTime(article.get('body'));
const totalReadTime = computeArticleReadingTime(
article.get('body'),
);
article.set('totalReadTime', totalReadTime);
},
beforeUpdate(article) {
const totalReadTime = computeArticleReadingTime(article.get('body'));
const totalReadTime = computeArticleReadingTime(
article.get('body'),
);
article.set('totalReadTime', totalReadTime);
}
}
}
},
},
},
);
Article.associate = (models) => {
Article.belongsToMany(models.User, {
Expand Down
10 changes: 9 additions & 1 deletion src/routers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
reportArticle,
bookmarkArticle,
editArticleTag,
rateArticle
rateArticle,
publishArticle
} from '../controllers/article';
import { addComment } from '../controllers/comment';
import checkFields from '../middlewares/auth/loginValidator';
Expand Down Expand Up @@ -167,6 +168,13 @@ router.get('/authors', Auth.authenticateUser, getAuthors);

router.post('/articles/:articleId/ratings', Auth.authenticateUser, articleRatingValidation, rateArticle);

router.patch(
'/articles/:articleId/publish',
Auth.authenticateUser,
verifyArticle,
isAuthor,
publishArticle,
);

router.all('*', (req, res) => {
res.status(404).json({
Expand Down
8 changes: 5 additions & 3 deletions src/utils/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
* @name findById
* @description This is function for getting records by primary key
* @param {object} Model The object model
* @param {string} id The primary key
* @param {object} id Containing the primary key
* @param {object} conditions Containing optional conditions
* @returns {object} Returns record query
*/
export const findById = async (Model, id) => {
const record = await Model.findByPk(id);
export const findById = async (Model, { id, articleId, }, conditions = null) => {
id = !id ? articleId : id;
const record = await Model.findByPk(id, { where: conditions });
return record;
};

Expand Down
33 changes: 32 additions & 1 deletion tests/integration/article.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe('DELETE /api/v1/articles/:id', () => {
.set('Authorization', JWT_TOKEN)
.end((_err, res) => {
const { body } = res;
expect(res).to.have.status(202);
expect(res).to.have.status(200);
expect(body).should.be.an('object');
expect(body).to.have.property('status');
expect(body).to.have.property('message');
Expand Down Expand Up @@ -241,6 +241,7 @@ describe('POST /api/v1/articles/:id/highlight', () => {
app = await startServer(5000);
agent = chai.request(app);
});

it('Should return status: 201', (done) => {
agent
.post('/api/v1/articles/a7f6cbad-db13-4531-a0e2-498f1c30766e/highlight')
Expand All @@ -258,6 +259,7 @@ describe('POST /api/v1/articles/:id/highlight', () => {
done();
});
});

after(async (done) => {
app.close();
app = null;
Expand All @@ -270,6 +272,7 @@ describe('POST /api/v1/articles/:id/highlight', () => {
app = await startServer(5000);
agent = chai.request(app);
});

it('Should return status: 403 when startIndex and stopIndex is not passed', (done) => {
agent
.post('/api/v1/articles/a7f6cbad-db13-4531-a0e2-498f1c30766e/highlight')
Expand All @@ -283,6 +286,7 @@ describe('POST /api/v1/articles/:id/highlight', () => {
done();
});
});

after(async (done) => {
app.close();
app = null;
Expand Down Expand Up @@ -318,3 +322,30 @@ describe('POST /api/v1/articles/:id/highlight', () => {
done();
});
});

describe('POST /api/v1/articles/:id/publish', () => {
beforeEach(async () => {
app = await startServer(5000);
agent = chai.request(app);
});

it('Should return status: 200 when Article not on the table', (done) => {
agent
.patch('/api/v1/articles/979eaa2e-5b8f-4103-8192-4639afae2ba8/publish')
.set('Authorization', JWT_TOKEN)
.end((_err, res) => {
const { body } = res;
expect(res).to.have.status(200);
expect(body).should.be.an('object');
expect(body).to.have.property('status');
expect(body).to.have.property('message');
done();
});
});

after(async (done) => {
app.close();
app = null;
done();
});
});

0 comments on commit 1be1a42

Please sign in to comment.