-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #31 from andela/ft-user-create-article-164489786
Ft user create article 164489786
- Loading branch information
Showing
10 changed files
with
229 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import models from '../models'; | ||
import Slug from '../helpers/slug'; | ||
|
||
const { article: ArticleModel } = models; | ||
/** | ||
* @description CRUD for article Class | ||
*/ | ||
class Article { | ||
/** | ||
*@author: Innocent Nkunzi | ||
* @param {Object} req | ||
* @param {Object} res | ||
* @returns {Object} Article | ||
*/ | ||
static async createArticle(req, res) { | ||
if (!req.body.title) { | ||
return res.status(400).json({ error: 'title can not be null' }); | ||
} | ||
const slugInstance = new Slug(req.body.title); | ||
const descriptData = req.body.description || `${req.body.body.substring(0, 100)}...`; | ||
try { | ||
const { | ||
title, body, taglist | ||
} = req.body; | ||
const slug = slugInstance.returnSlug(title); | ||
const authorid = 100; | ||
const newArticle = { | ||
title, body, description: descriptData, slug, authorid, taglist | ||
}; | ||
const article = await ArticleModel.createArticle(newArticle); | ||
return res.status(201).json({ article }); | ||
} catch (error) { return res.status(400).json({ message: error.errors[0].message }); } | ||
} | ||
} | ||
export default Article; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import slugify from 'slugify'; | ||
import hasha from 'hasha'; | ||
/** | ||
* This class to maake a slug | ||
*/ | ||
class Slug { | ||
/** | ||
* @author: Innocent Nkunzi | ||
* @param {param} strings | ||
* @returns {*} returns a slug | ||
*/ | ||
constructor(strings) { | ||
this.strings = strings; | ||
this.randomNumber = Math.floor(Math.random() * 10000); | ||
this.randomString = Math.random().toString(36).substr(2, 1); | ||
this.date = new Date(); | ||
this.slugMaker(); | ||
} | ||
|
||
/** | ||
* @author: Innocent Nkunzi | ||
* @returns {*} it returns a slug without an ID | ||
*/ | ||
slugMaker() { | ||
if (this.strings.length <= 40) { | ||
return slugify(this.strings); | ||
} | ||
const slug = this.strings.substring(0, 40); | ||
return slugify(slug); | ||
} | ||
|
||
/** | ||
* @author: Innocent Nkunzi | ||
* @returns {*} it returns a full slug with a hashed ID | ||
*/ | ||
returnSlug() { | ||
const slug = this.slugMaker(); | ||
const thisDate = Date.now().toString(); | ||
|
||
let nowHash = hasha(thisDate); | ||
nowHash = nowHash.substring(0, 7); | ||
const newSlug = `${slug}-${nowHash}`; | ||
return newSlug; | ||
} | ||
} | ||
|
||
export default Slug; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,30 @@ | ||
module.exports = (sequelize, DataTypes) => { | ||
import sequelizeTrasform from 'sequelize-transforms'; | ||
|
||
const ArticleModel = (sequelize, DataTypes) => { | ||
const Article = sequelize.define('article', { | ||
id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, | ||
slug: { type: DataTypes.STRING, allowNull: false, unique: true }, | ||
title: { type: DataTypes.STRING, allowNull: false }, | ||
body: { type: DataTypes.TEXT, allowNull: false }, | ||
taglist: { type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true }, | ||
description: { type: DataTypes.TEXT, allowNull: true }, | ||
title: { | ||
type: DataTypes.STRING, | ||
allowNull: false, | ||
trim: true, | ||
validate: { len: { args: 5 }, notEmpty: true } | ||
}, | ||
body: { | ||
type: DataTypes.TEXT, trim: true, allowNull: false, validate: { len: { args: 255, msg: 'Body needs to be above 255 characters' }, notEmpty: true } | ||
}, | ||
taglist: { type: DataTypes.ARRAY(DataTypes.STRING), allowNull: true, defaultValue: [] }, | ||
description: { type: DataTypes.TEXT, trim: true }, | ||
authorid: { type: DataTypes.INTEGER, allowNull: false } | ||
}, {}); | ||
sequelizeTrasform(Article); | ||
Article.createArticle = article => Article.create(article); | ||
|
||
Article.associate = (models) => { | ||
Article.belongsTo(models.user, { | ||
foreignKey: 'authorid', onDelete: 'CASCADE' | ||
}); | ||
}; | ||
return Article; | ||
}; | ||
export default ArticleModel; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import chai from 'chai'; | ||
import faker from 'faker'; | ||
import chaiHttp from 'chai-http'; | ||
import db from '../models'; | ||
import fakeData from './mockData/articleMockData'; | ||
import index from '../index'; | ||
|
||
const articleModel = db.article; | ||
const userModel = db.user; | ||
|
||
chai.should(); | ||
chai.use(chaiHttp); | ||
|
||
/** | ||
* @author: Innocent Nkunzi | ||
* @description: tests related to article | ||
*/ | ||
|
||
before('Cleaning the database first', async () => { | ||
await articleModel.destroy({ truncate: true, cascade: true }); | ||
await userModel.destroy({ where: { email: userModel.email }, truncate: true, cascade: true }); | ||
}); | ||
const user = { | ||
id: 100, | ||
username: 'nkunziinnocent', | ||
email: 'nkunzi@gmail.com', | ||
password: '@Nkunzi1234', | ||
}; | ||
describe('Create a user to be used in in creating article', () => { | ||
it('should create a user', (done) => { | ||
chai.request(index).post('/api/auth/signup').send(user).then((res) => { | ||
res.should.have.status(200); | ||
res.body.user.should.be.a('object'); | ||
res.body.user.should.have.property('username'); | ||
done(); | ||
}) | ||
.catch(err => err); | ||
}).timeout(15000); | ||
}); | ||
describe('Create an article', () => { | ||
it('should create an article', (done) => { | ||
chai.request(index).post('/api/articles').send(fakeData).then((res) => { | ||
res.should.have.status(201); | ||
res.body.should.have.property('article'); | ||
res.body.article.should.be.a('object'); | ||
res.body.article.should.have.property('id'); | ||
res.body.article.should.have.property('slug'); | ||
res.body.article.should.have.property('title'); | ||
res.body.article.should.have.property('description'); | ||
res.body.article.should.have.property('createdAt'); | ||
res.body.article.should.have.property('updatedAt'); | ||
done(); | ||
}) | ||
.catch(err => err); | ||
}).timeout(15000); | ||
}); | ||
describe('It checks title errors', () => { | ||
it('Should not create an article if the title is empty', (done) => { | ||
const newArticle = { | ||
title: '', | ||
description: faker.lorem.paragraph(), | ||
body: faker.lorem.paragraphs(), | ||
}; | ||
chai.request(index).post('/api/articles').send(newArticle).then((res) => { | ||
res.should.have.status(400); | ||
res.body.should.be.a('object'); | ||
res.body.should.have.property('error').eql('title can not be null'); | ||
done(); | ||
}) | ||
.catch(err => err); | ||
}); | ||
}); | ||
describe('Test the body', () => { | ||
it('should not create and article if the body is empty', (done) => { | ||
const newArticle = { | ||
title: faker.random.words(), | ||
description: faker.lorem.paragraph(), | ||
body: '' | ||
}; | ||
chai.request(index).post('/api/articles').send(newArticle).then((res) => { | ||
res.should.have.status(400); | ||
res.body.should.be.a('object'); | ||
// res.body.should.have.property('error').eql('The body can\'t be empty'); | ||
done(); | ||
}) | ||
.catch(err => err); | ||
}); | ||
it('should return an error if the body is not predefined', (done) => { | ||
const longTitleArticle = { | ||
title: faker.lorem.sentences(), | ||
description: faker.lorem.paragraph(), | ||
}; | ||
chai.request(index).post('/api/articles').send(longTitleArticle).then((res) => { | ||
res.should.have.status(400); | ||
res.body.should.be.a('object'); | ||
res.body.should.have.property('message').eql('article.body cannot be null'); | ||
done(); | ||
}) | ||
.catch(err => err); | ||
}); | ||
}); | ||
describe('Test the title', () => { | ||
it('should substring a long title to only 40 characters', (done) => { | ||
const longTitleArticle = { | ||
title: faker.lorem.sentence(), | ||
body: faker.lorem.paragraphs(), | ||
description: faker.lorem.paragraph(), | ||
}; | ||
chai.request(index).post('/api/articles').send(longTitleArticle).then((res) => { | ||
res.should.have.status(201); | ||
res.body.should.be.a('object'); | ||
res.body.should.have.property('article'); | ||
done(); | ||
}) | ||
.catch(err => err); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters