Skip to content

Commit

Permalink
Merge pull request #20 from andela/ft-article-tagging-159987411
Browse files Browse the repository at this point in the history
#159987411 User can tag an article and view articles
  • Loading branch information
Inumidun Amao committed Sep 19, 2018
2 parents ab3c40f + 5d0e7f5 commit 6c9af2f
Show file tree
Hide file tree
Showing 20 changed files with 409 additions and 183 deletions.
7 changes: 1 addition & 6 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,9 @@ pids
*.pid
*.seed

#bash file
export_env.bash

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# vscode
.vscode

# Coverage directory used by tools like istanbul
coverage

Expand Down Expand Up @@ -51,6 +45,7 @@ package-lock.json

# bash file
export_env.bash

#vscode files
.vscode

Expand Down
9 changes: 2 additions & 7 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import articleRoutes from './routes/articleRoutes';
import passportSetup from './config/passportSetup';
import authRoutes from './routes/authRoutes';
import caseRoutes from './routes/caseRoutes';
import tagRoute from './routes/tagRoutes';

dotenv.config();
const app = express();
Expand All @@ -19,27 +20,21 @@ const port = parseInt(process.env.PORT, 10) || 8000;

// USE CORS TO AVOID CROSS ORIGIN CONFLICT
app.use(cors());
app.use(jsend.middleware);

// USE SWAGGER DOCUMENTATION
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

// USE JSEND MIDDLEWARE
app.use(jsend.middleware);

// ALLOW APP TO USE BODY-PARSER.
app.use(urlencoded);
app.use(json);
app.use(jsend.middleware);


app.use(passportSetup.initialize());

// Use user routes
app.use('/api/v1/users', userRoutes);
app.use('/api/v1/users/auth', authRoutes);
app.use('/api/v1/articles', articleRoutes);
app.use('/api/v1/cases', caseRoutes);
app.use('/api/v1/tags', tagRoute);

app.get('/', (req, res) => res.status(200).jsend.success({
message: 'Welcome to the sims program'
Expand Down
21 changes: 16 additions & 5 deletions server/controllers/articleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import ratingHelpers from '../helpers/ratingHelpers';
import models from '../models';
import { dataUri } from '../config/multer/multerConfig';
import imageUpload from '../helpers/imageUpload';
import tagManager from '../helpers/tagManager';

const {
Cases,
Articles,
Ratings,
ArticleLikes
ArticleLikes,
Tags
} = models;
const { analyseRatings } = ratingHelpers;

Expand Down Expand Up @@ -43,10 +45,19 @@ const articlesController = {
description: fields.description,
body: fields.body,
imageUrl
}).then(createdArticle => res.status(201).jsend.success({
article: createdArticle,
message: 'Article published successfully'
})).catch(err => res.status(500).jsend.fail({
}).then((createdArticle) => {
// checks if tags exist
const tags = !fields.tags || fields.tags === ''
? []
: fields.tags.replace(/\s+/g, '').split(',');

tagManager.createTag(res, tags, Tags, createdArticle);
return res.status(201).jsend.success({
article: createdArticle,
tags,
message: 'Article published successfully'
});
}).catch(err => res.status(500).jsend.fail({
message: 'Something, went wrong. please try again',
error: err.message
}));
Expand Down
4 changes: 2 additions & 2 deletions server/controllers/searchController.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import search from '../helpers/search';
const { escape } = validator;
const { getIds, searcher } = search;
const {
Articles, Users, Tags, Categories, ArticleTags
Articles, Users, Tags, Categories, ArticlesTags
} = models;

const searchController = (req, res) => {
Expand Down Expand Up @@ -62,7 +62,7 @@ const searchController = (req, res) => {
}],
...searchOptions
};
return searcher(res, ArticleTags, queryOptions, currentPage, 'tag', escape(tag));
return searcher(res, ArticlesTags, queryOptions, currentPage, 'tag', escape(tag));
});
}

Expand Down
36 changes: 36 additions & 0 deletions server/controllers/tagController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import models from '../models';

const { Articles, Tags } = models;

const tagController = {
all: (req, res) => {
Tags
.findAll()
.then(tags => res.status(200).jsend.success({ tags }))
.catch(() => res.status(500).jsend.error({ message: 'Your request cannot be completed at the moment. please try again.' }));
},

single: (req, res) => {
Tags
.findOne({
where: { name: req.params.tagName },
include: [{
model: Articles,
as: 'articles',
attributes: ['id', 'title', 'slug'],
through: {
attributes: ['createdAt']
}
}]
})
.then((tag) => {
if (!tag) {
return res.status(404).jsend.fail({ message: `tag with name ${req.params.tagName} not found` });
}
return res.status(200).jsend.success({ tag });
})
.catch(() => res.status(500).jsend.error({ message: 'Your request cannot be completed at the moment. please try again.' }));
}

};
export default tagController;
26 changes: 26 additions & 0 deletions server/helpers/tagManager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* @description Function to create tags from array of strings
* and add them to the article.
* @param {object} res
* @param {Array} tagsArray
* @param {Model} tagsModel
* @param {Model instance} article
* @returns {object} undefined
*/const tagManager = {
createTag: (res, tagsArray, tagsModel, article) => {
tagsArray.forEach((tag) => {
tagsModel
.findOrCreate({
where: {
name: tag
}
})
.spread((createdTag) => {
article.addTags(createdTag);
})
.catch(() => res.status(500).jsend.fail({ message: 'Something, went wrong. please try again' }));
});
}
};

export default tagManager;
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('ArticleTags', {
up: (queryInterface, Sequelize) => queryInterface.createTable('ArticlesTags', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
articleId: {
tagId: {
type: Sequelize.INTEGER,
references: {
model: 'Articles',
model: 'Tags',
key: 'id'
}
},
tagId: {
articleId: {
type: Sequelize.INTEGER,
references: {
model: 'Tags',
model: 'Articles',
key: 'id'
}
},
Expand All @@ -29,5 +29,6 @@ module.exports = {
type: Sequelize.DATE
}
}, { freezeTableName: true }),
down: queryInterface => queryInterface.dropTable('ArticleTags')

down: queryInterface => queryInterface.dropTable('ArticlesTags')
};
24 changes: 0 additions & 24 deletions server/models/ArticleTags.js

This file was deleted.

4 changes: 2 additions & 2 deletions server/models/articles.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ const articles = (sequelize, DataTypes) => {
});

Articles.belongsToMany(models.Tags, {
as: 'articleTags',
through: 'ArticleTags',
as: 'tags',
through: models.ArticlesTags,
foreignKey: 'articleId'
});

Expand Down
22 changes: 22 additions & 0 deletions server/models/articlesTags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = (sequelize, DataTypes) => {
const ArticlesTags = sequelize.define('ArticlesTags', {
tagId: {
type: DataTypes.INTEGER
},
articleId: {
type: DataTypes.INTEGER
}
});
ArticlesTags.associate = (models) => {
ArticlesTags.belongsTo(models.Articles, {
foreignKey: 'articleId',
onDelete: 'CASCADE'
});

ArticlesTags.belongsTo(models.Tags, {
foreignKey: 'tagId',
onDelete: 'CASCADE'
});
};
return ArticlesTags;
};
4 changes: 2 additions & 2 deletions server/models/tags.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ const tags = (sequelize, DataTypes) => {

Tags.associate = (models) => {
Tags.belongsToMany(models.Articles, {
as: 'tagArticle',
through: 'ArticleTags',
as: 'articles',
through: models.ArticlesTags,
foreignKey: 'tagId'
});
};
Expand Down
11 changes: 11 additions & 0 deletions server/routes/tagRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import express from 'express';
import tagController from '../controllers/tagController';
import auth from '../middleware/auth';

const route = express.Router();

// GET TAG ROUTE
route.get('/', auth, tagController.all);
route.get('/:tagName', auth, tagController.single);

export default route;
4 changes: 2 additions & 2 deletions server/seeders/20180910285904-demo-articletags.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = {
up: queryInterface => queryInterface.bulkInsert('ArticleTags', [{
up: queryInterface => queryInterface.bulkInsert('ArticlesTags', [{
articleId: 1,
tagId: 1,
createdAt: '2018-09-08',
Expand All @@ -17,5 +17,5 @@ module.exports = {
createdAt: '2018-09-08',
updatedAt: '2018-09-08'
}]),
down: queryInterface => queryInterface.bulkDelete('ArticleTags')
down: queryInterface => queryInterface.bulkDelete('ArticlesTags')
};
12 changes: 12 additions & 0 deletions server/seeders/20180911155134-seed-tagger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
module.exports = {
up: queryInterface => queryInterface.bulkInsert('Users', [{
username: 'tagger',
email: 'tagger@gmail.com',
password: '$2b$08$Hkj0DXWIk0k/M3lTZWx/luvSTBY3d1DeArh2Zp6SruHoDtMJiBXfS',
isVerified: true,
createdAt: '2018-09-08',
updatedAt: '2018-09-08'
}]),

down: queryInterface => queryInterface.bulkDelete('Users')
};
14 changes: 14 additions & 0 deletions server/seeders/20180911175232-tags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports = {
up: queryInterface => queryInterface.bulkInsert('Tags', [{
name: 'javascript',
createdAt: '2018-09-08',
updatedAt: '2018-09-08'
},
{
name: 'nodejs',
createdAt: '2018-09-08',
updatedAt: '2018-09-08'
}]),

down: queryInterface => queryInterface.bulkDelete('Tags')
};
Loading

0 comments on commit 6c9af2f

Please sign in to comment.