diff --git a/controllers/article.js b/controllers/article.js index d2379cd..a355591 100644 --- a/controllers/article.js +++ b/controllers/article.js @@ -91,8 +91,7 @@ class ArticleController { article.dataValues.readingTime = readingTime(article.title + article.body); // @return article return res.status(200).json({ status: 200, article }); - }) - .catch(error => res.status(500).json({ error: `Something wrong please try again later. ${error}` })); + }); } } diff --git a/controllers/search.js b/controllers/search.js new file mode 100644 index 0000000..ca7a1b8 --- /dev/null +++ b/controllers/search.js @@ -0,0 +1,46 @@ +import Sequelize from 'sequelize'; +import model from '../models/index'; +import TagData from '../helpers/tags'; + +const { Op } = Sequelize; +// @assign model +const User = model.user; +const Article = model.article; +/** + * @param {class} --Search controller + */ +class searchController { +/** + * + * @param {Object} req + * @param {Object} res + * @returns {Object} all item related to item + */ + static async searchItem(req, res) { + // item from req.query + const { search } = req.query; + try { + // first lets search from user + const user = await User.findAll({ + attributes: { exclude: ['password', 'isAdmin', 'isActivated', 'createdAt', 'updatedAt'] }, + where: { username: { [Op.like]: `%${search}%` } }, + include: [{ model: Article }] + }); + const article = await Article.findAll({ + where: { title: { [Op.like]: `%${search}%` } }, + include: [{ + model: User, + as: 'authorfkey', + attributes: { exclude: ['password', 'isAdmin', 'isActivated', 'createdAt', 'updatedAt'] } + }] + }); + const tags = await TagData(req); + return res.status(200).json({ + status: 200, user, article, tags + }); + } catch (error) { + return res.status(500).json({ error: `something wrong try again later ${error}` }); + } + } +} +export default searchController; diff --git a/helpers/search.js b/helpers/search.js new file mode 100644 index 0000000..3a84b05 --- /dev/null +++ b/helpers/search.js @@ -0,0 +1,9 @@ +import Validator from './validateUser'; + +const search = (req, res, next) => { + if (Validator.isEmpty(req.query.search)) { + return res.status(400).json({ error: ' no result found.'}); + } + next(); +}; +export default search; diff --git a/helpers/tags.js b/helpers/tags.js new file mode 100644 index 0000000..4b9cd91 --- /dev/null +++ b/helpers/tags.js @@ -0,0 +1,24 @@ +import model from '../models/index'; + +const Article = model.article; +const tagFetch = async (req) => { + const { search } = req.query; + const arr = []; + const taglist = []; + const tag = await Article.findAll({ attributes: ['taglist'] }); + tag.forEach((element) => { + const list = element.taglist; + list.forEach((ls) => { + arr.push(ls); + }); + }); + // find in tags + for (let i = 0; i < arr.length; i++) { + if (arr[i].indexOf(search) !== -1) { + taglist.push(arr[i]); + } + } + return taglist; +}; + +export default tagFetch; diff --git a/routes/api/search.js b/routes/api/search.js new file mode 100644 index 0000000..2b86c18 --- /dev/null +++ b/routes/api/search.js @@ -0,0 +1,13 @@ +import express from 'express'; +import Search from '../../controllers/search'; +import validation from '../../helpers/search'; + +const router = express.Router(); + +// @method POST +// @desc search item +// @access public + +router.post('/', validation, Search.searchItem); + +export default router; diff --git a/routes/index.js b/routes/index.js index 108fba3..804cfa6 100644 --- a/routes/index.js +++ b/routes/index.js @@ -7,6 +7,7 @@ import twitterAuth from './api/auth/twitter'; import users from './api/users'; import article from './api/article'; +import search from './api/search'; // @api // @ initialize app @@ -16,6 +17,7 @@ app.use('/api/users/auth', facebookAuth); app.use('/api/users', twitterAuth); app.use('/api/users', users); app.use('/api/articles', article); +app.use('/api/search', search); // @swagger UI app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); app.use('/api/users', users); diff --git a/test/2-user.js b/test/2-user.js index 23d45c2..43cc071 100644 --- a/test/2-user.js +++ b/test/2-user.js @@ -6,6 +6,7 @@ import { signup1, signup3, signup4, + signup5, login1, login2, login3, @@ -29,6 +30,7 @@ let token; describe('User ', () => { before(async () => { await User.destroy({ where: { email: signup1.email } }); + await User.destroy({ where: { email: signup5.email } }); await User.destroy({ where: { email: 'pacifiqueclement@gmail.com' } }); await User.destroy({ where: { email: 'jeandedieuam@gmail.com' } }); }); @@ -46,6 +48,22 @@ describe('User ', () => { done(); }); }).timeout(20000); + // @second user + it('Should create user and return status of 201', (done) => { + chai + .request(app) + .post('/api/users') + .set('Content-Type', 'application/json') + .send(signup5) + .end((err, res) => { + if (err) done(err); + res.should.have.status(201); + res.body.should.have.property('token'); + res.body.should.have.property('username'); + done(); + }); + }).timeout(50000); + // it('Should return status of 409', (done) => { chai .request(app) diff --git a/test/3-artilcle.js b/test/3-artilcle.js index 5a9bf46..a4f8438 100644 --- a/test/3-artilcle.js +++ b/test/3-artilcle.js @@ -2,14 +2,15 @@ import chai from 'chai'; import chaiHttp from 'chai-http'; import app from '../index'; import { article1 } from '../testingdata/article.json'; -import { login1 } from '../testingdata/user.json'; import shareArticle from '../helpers/shareArticles'; +import { login1, login4 } from '../testingdata/user.json'; import models from '../models/index'; chai.use(chaiHttp); chai.should(); const Article = models.article; let APItoken; +let APItoken2; let articleId; const facebookUrl = { req: { @@ -40,6 +41,8 @@ describe('Article', () => { await Article.destroy({ where: { title: article1.title } }); const tokens = await chai.request(app).post('/api/users/login').set('Content-Type', 'application/json').send(login1); APItoken = `Bearer ${tokens.body.token}`; + const login2 = await chai.request(app).post('/api/users/login').set('Content-Type', 'application/json').send(login4); + APItoken2 = `Bearer ${login2.body.token}`; } catch (error) { console.log(error); } @@ -142,11 +145,30 @@ describe('Article', () => { done(); }); }); + // should return status code of 403 + it('Should return status code of 403', (done) => { + chai.request(app) + .put(`/api/articles/${articleId}`) + .set('Authorization', APItoken2) + .set('Content-Type', 'multipart/form-data') + .field('title', article1.title) + .field('body', article1.body) + .field('tag', article1.tag) + .attach('image', '') + .end((error, res) => { + if (error) { + done(error); + } + res.should.have.status(403); + res.body.should.have.property('error'); + done(); + }); + }); // @should return 404 // @article not found it('Should return status code of 404', (done) => { chai.request(app) - .put('/api/articles/500') + .put('/api/articles/5007') .set('Authorization', APItoken) .set('Content-Type', 'multipart/form-data') .field('title', article1.title) @@ -161,6 +183,23 @@ describe('Article', () => { done(); }); }); + it('Should return status code of 500', (done) => { + chai.request(app) + .put('/api/articles/5007458674867495769456745') + .set('Authorization', APItoken) + .set('Content-Type', 'multipart/form-data') + .field('title', article1.title) + .field('body', article1.body) + .field('tag', article1.tag) + .attach('image', '') + .end((error, res) => { + if (error) { + done(error); + } + res.should.have.status(500); + done(); + }); + }); // @delete article // @ return status code of 200 it('should return status code of 200 on deleting article', (done) => { @@ -201,5 +240,35 @@ describe('Article', () => { it('Should allow the user to share an articles across linkedin channel', async () => { const result = await shareArticle.openChannelUrl(linkedUrl.req); result.should.be.a('object'); - }); }); + // @search + // should return status of 400 + it('Should return status code of 400', (done) => { + chai.request(app) + .post('/api/search/?search=') + .set('Content-Type', 'application/json') + .end((error, res) => { + if (error) { + done(error); + } + res.should.have.status(400); + res.body.should.have.property('error'); + done(); + }); + }); + it('Should return status code of 200', (done) => { + chai.request(app) + .post('/api/search/?search=a') + .set('Content-Type', 'application/json') + .end((error, res) => { + if (error) { + done(error); + } + res.should.have.status(200); + res.body.should.have.property('article'); + res.body.should.have.property('user'); + res.body.should.have.property('tags'); + done(); + }); + }); +}) diff --git a/testingdata/article.json b/testingdata/article.json index 4ae10d9..e2adb9e 100644 --- a/testingdata/article.json +++ b/testingdata/article.json @@ -2,6 +2,6 @@ "article1": { "title": "why i love javascript", "body": " i love javascript so much?", - "tag":"laravel" + "tag":"laravel,programming" } } diff --git a/testingdata/user.json b/testingdata/user.json index 946f9bf..079437f 100644 --- a/testingdata/user.json +++ b/testingdata/user.json @@ -18,6 +18,11 @@ "email": "kaleb@gmail.com", "password": "1Kig1L@20" }, + "signup5": { + "username": "gratian", + "email": "gratian@gmail.com", + "password": "1Kig1L@20" + }, "testMailer": { "username": "badass", "email": "badass.andela.test@gmail.com", @@ -39,8 +44,12 @@ "login3": { "email": "blaise@gmail.com" }, + "login4":{ + "email": "gratian@gmail.com", + "password": "1Kig1L@20" + }, "googleValidToken": { - "access_token": "ya29.GlzvBsYBoHEEyCgpoljnA2IBEvb6IvbEvXUFnhGR1aakPOl0l3Kgfv6uuBtSCyRp69HeDpMbbc9pxZy1Lpy1dNUpDnwBzyI4p_WH1I_rKI3QchpiOTqDi42nxHhlOg" + "access_token": "ya29.GlvvBqe7M-RLlus5dSVWVvHjTRlsfHrqPfww-d9urRPLj0q18SMMdDcFcW4MeoY6tP0XwOSeKcjW2yzPGgqx7hL7-QgEiKnu40OdXHyPGFBt5CT2LnqKHju7Wko7" }, "googleInvalidToken": { "access_token": "ya29.GlvhBpzY2hl2ShgOMrpkni8obGgwyX0mr85Oendf2kmblu3BrRNTmYK2DVQiPciVOBFkLvR57YE90qDyffgJOqgzV68zutO3-Y9QDKooAPuxPvwsbsWM36wwVPHT"