From d8ba1abfa23035fefa88c335552856a70f37c9b7 Mon Sep 17 00:00:00 2001 From: nshutijonathan Date: Mon, 21 Oct 2019 10:27:13 +0200 Subject: [PATCH] ft(articles):get articles for specific user[Finishes#169254678] --- src/controllers/articles.controller.js | 237 ++++++++++++++++------- src/routes/api/article/article.routes.js | 110 +++++++++-- test/test.articles.js | 44 ++++- 3 files changed, 293 insertions(+), 98 deletions(-) diff --git a/src/controllers/articles.controller.js b/src/controllers/articles.controller.js index f97de7b..c5426e2 100644 --- a/src/controllers/articles.controller.js +++ b/src/controllers/articles.controller.js @@ -24,14 +24,14 @@ const db = models.Article; */ class Articles { /** - * - * - * @static - * @param {*} req - * @param {*} res - * @returns {object} data - * @memberof Articles - */ + * + * + * @static + * @param {*} req + * @param {*} res + * @returns {object} data + * @memberof Articles + */ static async createArticles(req, res) { const userId = req.auth.id; const findUser = await Userservice.getOneUser(userId); @@ -69,24 +69,33 @@ class Articles { } /** - * - * - * @static - * @param {*} req - * @param {*} res - * @returns {object} articles - * @memberof Articles - */ + * + * + * @static + * @param {*} req + * @param {*} res + * @returns {object} articles + * @memberof Articles + */ static async getAllArticles(req, res) { const counter = await db.count(); if (req.offset >= counter) { req.offset = 0; } const { searchQueries, offset, limit } = req; - const articles = await articleService.getAllArticles(offset, limit, searchQueries); + const articles = await articleService.getAllArticles( + offset, + limit, + searchQueries + ); if (!articles) { - return res.status(200).json({ status: 200, message: 'There is no article.' }); + return res + .status(200) + .json({ status: 200, message: 'There is no article.' }); } + // if (!(articles[0].authorId === req.auth.id)) { + // return res.status(200).json({ status: 200, message: 'no articles.' }); + // } const allArticles = _.map( articles, _.partialRight(_.pick, [ @@ -101,31 +110,37 @@ class Articles { 'flagged', 'images', 'views', - 'createdAt', + 'createdAt' ]) ); - await Promise.all(allArticles.map(async (article) => { - try { - const userDetails = await Userservice.getOneUser(article.authorId); - const { username, image } = userDetails; - const readTime = Helper.calculateReadTime(article.body); - const timeAgo = moment(article.createdAt).fromNow(); - article.readtime = readTime; - article.username = username; - article.userImage = image; - article.timeCreated = timeAgo; - return true; - } catch (error) { - console.log(error); - } - })); + await Promise.all( + allArticles.map(async article => { + try { + const userDetails = await Userservice.getOneUser(article.authorId); + const { username, image } = userDetails; + const readTime = Helper.calculateReadTime(article.body); + const timeAgo = moment(article.createdAt).fromNow(); + article.readtime = readTime; + article.username = username; + article.userImage = image; + article.timeCreated = timeAgo; + return true; + } catch (error) { + console.log(error); + } + }) + ); const popularArticles = allArticles.slice(0); popularArticles.sort((a, b) => b.views - a.views); const mostPopular = popularArticles.slice(0, 9); if (req.query.popular) { - util.setSuccess(200, 'The most popular articles on authors haven', mostPopular); + util.setSuccess( + 200, + 'The most popular articles on authors haven', + mostPopular + ); return util.send(res); } return res.status(200).json({ @@ -135,16 +150,80 @@ class Articles { data: allArticles }); } + static async SpecificUserArticles(req, res) { + const counter = await db.count(); + if (req.offset >= counter) { + req.offset = 0; + } + const { searchQueries, offset, limit } = req; + const articles = await articleService.getAllArticles( + offset, + limit, + searchQueries + ); + if (!articles) { + return res + .status(200) + .json({ status: 200, message: 'There is no article.' }); + } + if (!(articles[0].authorId === req.auth.id)) { + return res.status(200).json({ status: 200, message: 'no articles.' }); + } + const allArticles = _.map( + articles, + _.partialRight(_.pick, [ + 'authorId', + 'slug', + 'title', + 'description', + 'body', + 'taglist', + 'favorited', + 'favoritedcount', + 'flagged', + 'images', + 'views', + 'createdAt' + ]) + ); + + await Promise.all( + allArticles.map(async article => { + try { + const userDetails = await Userservice.getOneUser(article.authorId); + const { username, image } = userDetails; + const readTime = Helper.calculateReadTime(article.body); + const timeAgo = moment(article.createdAt).fromNow(); + article.readtime = readTime; + article.username = username; + article.userImage = image; + article.timeCreated = timeAgo; + return true; + } catch (error) { + } + }) + ); + const popularArticles = allArticles.slice(0); + popularArticles.sort((a, b) => b.views - a.views); + const mostPopular = popularArticles.slice(0, 9); + + return res.status(200).json({ + status: 200, + message: 'List of all articles', + allArticle: counter, + data: allArticles + }); + } /** - * - * - * @static - * @param {*} req - * @param {*} res - * @returns {object} article - * @memberof Articles - */ + * + * + * @static + * @param {*} req + * @param {*} res + * @returns {object} article + * @memberof Articles + */ static async getOneArticle(req, res) { const findArticle = await db.findOne({ where: { slug: req.params.slug } @@ -166,7 +245,7 @@ class Articles { 'favoritedcount', 'flagged', 'images', - 'views', + 'views' ]); const readTime = Helper.calculateReadTime(article.body); article.readtime = readTime; @@ -206,14 +285,14 @@ class Articles { } /** - * - * - * @static - * @param {*} req - * @param {*} res - * @returns {object} message - * @memberof Articles - */ + * + * + * @static + * @param {*} req + * @param {*} res + * @returns {object} message + * @memberof Articles + */ static async deleteArticle(req, res) { const findArticle = await db.findOne({ where: { slug: req.params.slug } @@ -227,7 +306,8 @@ class Articles { if (req.auth.id !== findArticle.authorId) { return res.status(403).json({ status: 403, - message: 'Sorry you can not DELETE an article that does not belong to you.' + message: + 'Sorry you can not DELETE an article that does not belong to you.' }); } await db.destroy({ @@ -240,25 +320,28 @@ class Articles { } /** - * - * - * @static - * @param {*} req - * @param {*} res - * @returns {Object} updated article details - * @memberof Articles - */ + * + * + * @static + * @param {*} req + * @param {*} res + * @returns {Object} updated article details + * @memberof Articles + */ static async UpdateArticle(req, res) { const findArticle = await db.findOne({ where: { slug: req.params.slug } }); if (!findArticle) { - return res.status(200).json({ status: 200, message: 'That article does not exist' }); + return res + .status(200) + .json({ status: 200, message: 'That article does not exist' }); } if (req.auth.id !== findArticle.authorId) { return res.status(403).json({ status: 403, - message: 'Sorry you can not UPDATE an article that does not belong to you.' + message: + 'Sorry you can not UPDATE an article that does not belong to you.' }); } const { title, body, description } = req.body; @@ -276,14 +359,14 @@ class Articles { } /** - * - * - * @static - * @param {*} req - * @param {*} res - * @returns {Object} share article over email and social media channelds - * @memberof Articles - */ + * + * + * @static + * @param {*} req + * @param {*} res + * @returns {Object} share article over email and social media channelds + * @memberof Articles + */ static async shareArticle(req, res) { const article = await db.findOne({ where: { slug: req.params.slug } @@ -297,15 +380,21 @@ class Articles { const url = `${location}/articles/${req.params.slug}`; switch (req.params.channel) { case 'facebook': - await OpenUrlHelper.openUrl(`https:www.facebook.com/sharer/sharer.php?u=${url}`); + await OpenUrlHelper.openUrl( + `https:www.facebook.com/sharer/sharer.php?u=${url}` + ); util.setSuccess(200, `Article shared to ${req.params.channel}`, url); return util.send(res); case 'twitter': - await OpenUrlHelper.openUrl(`https://twitter.com/intent/tweet?url=${url}`); + await OpenUrlHelper.openUrl( + `https://twitter.com/intent/tweet?url=${url}` + ); util.setSuccess(200, `Article shared to ${req.params.channel}`, url); return util.send(res); case 'mail': - await OpenUrlHelper.openUrl(`mailto:?subject=${article.title}&body=${url}`); + await OpenUrlHelper.openUrl( + `mailto:?subject=${article.title}&body=${url}` + ); util.setSuccess(200, `Article shared to ${req.params.channel}`, url); return util.send(res); default: diff --git a/src/routes/api/article/article.routes.js b/src/routes/api/article/article.routes.js index 6aa662d..032eb1e 100644 --- a/src/routes/api/article/article.routes.js +++ b/src/routes/api/article/article.routes.js @@ -16,46 +16,122 @@ import StatsWare from '../../../middlewares/stats'; const router = express.Router(); const { - checkArticle, checkTagName, tagLength, tagLimit + checkArticle, checkTagName, tagLength, tagLimit } = TagWare; const { - createArticleTag, getArticleTags, editArticleTag, deleteArticleTag + createArticleTag, + getArticleTags, + editArticleTag, + deleteArticleTag } = tagController; -router.post('/:articleId/favorite', [auth, confirmEmailAuth, validateId], FavoritesController.createOrRemoveFavorite); -router.post('/', [auth, confirmEmailAuth], imageUpload.array('images', 10), validate(schema.articleSchema), articleController.createArticles); +router.post( + '/:articleId/favorite', + [auth, confirmEmailAuth, validateId], + FavoritesController.createOrRemoveFavorite +); +router.post( + '/', + [auth, confirmEmailAuth], + imageUpload.array('images', 10), + validate(schema.articleSchema), + articleController.createArticles +); router.get('/', checkQuery, articleController.getAllArticles); +router.get( + '/user/articles', + [auth], + checkQuery, + articleController.SpecificUserArticles +); router.get('/:slug', StatsWare.saveStat, articleController.getOneArticle); -router.delete('/:slug', [auth, confirmEmailAuth], articleController.deleteArticle); -router.patch('/:slug', [auth, confirmEmailAuth], imageUpload.array('images', 10), articleController.UpdateArticle); -router.post('/:slug/share/:channel', [auth, confirmEmailAuth], articleController.shareArticle); - +router.delete( + '/:slug', + [auth, confirmEmailAuth], + articleController.deleteArticle +); +router.patch( + '/:slug', + [auth, confirmEmailAuth], + imageUpload.array('images', 10), + articleController.UpdateArticle +); +router.post( + '/:slug/share/:channel', + [auth, confirmEmailAuth], + articleController.shareArticle +); // Highlight router.post('/:slug/highlight', [auth], highlight.bodyHighlightedText); router.delete('/highlight/:id', auth, highlight.deleteHighlightComment); router.get('/:articleId/highlight', auth, highlight.getHighlights); -router.get('/:id/highlight/share/:channel', [auth, share], highlight.shareHightlight); +router.get( + '/:id/highlight/share/:channel', + [auth, share], + highlight.shareHightlight +); // tags -router.post('/:articleId/tags', [auth, confirmEmailAuth], checkArticle, tagLimit, tagLength, createArticleTag); +router.post( + '/:articleId/tags', + [auth, confirmEmailAuth], + checkArticle, + tagLimit, + tagLength, + createArticleTag +); router.get('/:articleId/tags', checkArticle, getArticleTags); -router.patch('/:articleId/:name', [auth, confirmEmailAuth], checkArticle, checkTagName, editArticleTag); -router.delete('/:articleId/:name', [auth, confirmEmailAuth], checkArticle, checkTagName, deleteArticleTag); - +router.patch( + '/:articleId/:name', + [auth, confirmEmailAuth], + checkArticle, + checkTagName, + editArticleTag +); +router.delete( + '/:articleId/:name', + [auth, confirmEmailAuth], + checkArticle, + checkTagName, + deleteArticleTag +); // Highlight router.post('/:slug/highlight', [auth], highlight.bodyHighlightedText); router.delete('/highlight/:id', auth, highlight.deleteHighlightComment); router.get('/:articleId/highlight', auth, highlight.getHighlights); -router.get('/:id/highlight/share/:channel', [auth, share], highlight.shareHightlight); +router.get( + '/:id/highlight/share/:channel', + [auth, share], + highlight.shareHightlight +); // tags -router.post('/:articleId/tags', [auth, confirmEmailAuth], checkArticle, tagLimit, tagLength, createArticleTag); +router.post( + '/:articleId/tags', + [auth, confirmEmailAuth], + checkArticle, + tagLimit, + tagLength, + createArticleTag +); router.get('/:articleId/tags', checkArticle, getArticleTags); -router.patch('/:articleId/:name', [auth, confirmEmailAuth], checkArticle, checkTagName, editArticleTag); -router.delete('/:articleId/:name', [auth, confirmEmailAuth], checkArticle, checkTagName, deleteArticleTag); +router.patch( + '/:articleId/:name', + [auth, confirmEmailAuth], + checkArticle, + checkTagName, + editArticleTag +); +router.delete( + '/:articleId/:name', + [auth, confirmEmailAuth], + checkArticle, + checkTagName, + deleteArticleTag +); export default router; diff --git a/test/test.articles.js b/test/test.articles.js index 93699c6..6c559ea 100644 --- a/test/test.articles.js +++ b/test/test.articles.js @@ -20,7 +20,11 @@ describe('Articles', () => { .post('/api/v1/articles') .set('Content-Type', 'application/json') .set('authorization', usertoken) - .attach('images', fs.readFileSync(`${__dirname}/mock/pic.jpeg`), 'pic.jpeg') + .attach( + 'images', + fs.readFileSync(`${__dirname}/mock/pic.jpeg`), + 'pic.jpeg' + ) .end((err, res) => { expect(res.status).to.be.deep.equal(400); expect(res.body).to.have.deep.property('status'); @@ -36,13 +40,13 @@ describe('Articles', () => { files: 'nshuti', title: 'title1', description: 'hello world', - body: 'hello world', + body: 'hello world' } }; const res = { - status() { }, - send() { }, - json() { } + status() {}, + send() {}, + json() {} }; sinon.stub(res, 'status').returnsThis(); sinon.stub(cloudinary, 'generateCloudinaryUrl').returns([]); @@ -55,7 +59,10 @@ describe('Articles', () => { .get('/api/v1/articles') .end((err, res) => { expect(res.status).to.be.deep.equal(200); - expect(res.body).to.have.deep.property('message', 'List of all articles'); + expect(res.body).to.have.deep.property( + 'message', + 'List of all articles' + ); done(); }); }); @@ -65,7 +72,30 @@ describe('Articles', () => { .get('/api/v1/articles/fakeslug') .end((err, res) => { expect(res.status).to.be.deep.equal(200); - expect(res.body).to.have.deep.property('message', 'Article successfully retrieved'); + expect(res.body).to.have.deep.property( + 'message', + 'Article successfully retrieved' + ); + done(); + }); + }); + it('view list of articles for a specific user ', (done) => { + chai + .request(server) + .get('/api/v1/articles/user/articles') + .set('x-access-token', usertoken) + .end((err, res) => { + expect(res.status).to.be.deep.equal(200); + done(); + }); + }); + it('should return resource not found message if endpoint does not exist', (done) => { + chai + .request(server) + .get('/api/v1/articles/user/articless') + .end((error, res) => { + expect(res.status).to.be.equal(404); + expect(res.body).to.have.deep.property('error', 'Resource not found'); done(); }); });