Skip to content

Commit

Permalink
Merge pull request #43 from andela/ft-create-CRUD-operation-for-categ…
Browse files Browse the repository at this point in the history
…ory-resource-161114854

#161114854  Create CRUD operation for category resource
  • Loading branch information
Inumidun Amao committed Oct 18, 2018
2 parents 95e7db7 + ec0d2e3 commit 6ba4c93
Show file tree
Hide file tree
Showing 18 changed files with 1,266 additions and 718 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
"pretest": "NODE_ENV=test sequelize db:migrate && NODE_ENV=test sequelize db:seed:all && NODE_ENV=test npm run create_admin",
"test": "NODE_ENV=test nyc mocha test/**.spec.js --require babel-polyfill --require babel-core/register --exit",
"posttest": "NODE_ENV=test sequelize db:migrate:undo:all",
"heroku-postbuild": "npm run migrate && npm run create_admin",
"test:dev": "npm run posttest && npm test",
"start": "npm run create_admin && babel-node server/app.js",
"start": "babel-node server/app.js",
"start_dev": "NODE_ENV=development npm run create_admin && nodemon --exec babel-node server/app.js",
"migrate": "sequelize db:migrate",
"unmigrate": "sequelize db:migrate:undo:all",
Expand Down
10 changes: 8 additions & 2 deletions server/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ import caseRoutes from './routes/caseRoutes';
import tagRoute from './routes/tagRoutes';
import roleRoutes from './routes/roleRoutes';
import authorRoutes from './routes/authorRoutes';
import categoryRoute from './routes/categoryRoutes';

dotenv.config();
const app = express();
const urlencoded = bodyParser.urlencoded({ extended: false });
const urlencoded = bodyParser.urlencoded({
extended: false
});
const json = bodyParser.json();
const port = parseInt(process.env.PORT, 10) || 8000;

Expand All @@ -32,7 +35,9 @@ app.use(urlencoded);
app.use(json);
app.use(jsend.middleware);

app.use(expressSession({ secret: process.env.SECRET }));
app.use(expressSession({
secret: process.env.SECRET
}));
app.use(passportSetup.initialize());
app.use(passportSetup.session());

Expand All @@ -44,6 +49,7 @@ app.use('/api/v1/cases', caseRoutes);
app.use('/api/v1/tags', tagRoute);
app.use('/api/v1/roles', roleRoutes);
app.use('/api/v1/authors', authorRoutes);
app.use('/api/v1/categories', categoryRoute);

app.get('/', (req, res) => res.status(200).jsend.success({
message: 'Welcome to the sims program'
Expand Down
31 changes: 16 additions & 15 deletions server/controllers/articleController.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,23 +186,24 @@ const articlesController = {
const queryParams = req.paginationQueryParams;
// CALCULATE OFFSET
const offset = ((queryParams.page - 1) * queryParams.limit);
Articles.findAndCountAll({
offset,
limit: queryParams.limit
}).then((result) => {
const totalPages = Math.ceil(result.count / queryParams.limit);
Articles
.findAndCountAll({
offset,
limit: queryParams.limit
}).then((result) => {
const totalPages = Math.ceil(result.count / queryParams.limit);

return res.status(200).jsend.success({
message: 'Operation Successful',
articles: result.rows,
metadata: {
totalArticles: result.count,
currentPage: queryParams.page,
limit: queryParams.limit,
totalPages
}
return res.status(200).jsend.success({
message: 'Operation Successful',
articles: result.rows,
metadata: {
totalArticles: result.count,
currentPage: queryParams.page,
limit: queryParams.limit,
totalPages
}
});
});
});
},
/**
* @desc get Featured Articles
Expand Down
213 changes: 213 additions & 0 deletions server/controllers/categoryController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
import models from '../models';
import {
dataUri
} from '../config/multer/multerConfig';
import imageUpload from '../helpers/imageUpload';

const {
Articles,
Categories,
Users
} = models;

const categoryController = {
allCategory: (req, res) => {
const queryParams = req.paginationQueryParams;
const offset = ((queryParams.page - 1) * queryParams.limit);
Categories
.findAndCountAll({
attributes: ['id', 'name', 'poster', 'description'],
offset,
limit: queryParams.limit,
})
.then((categories) => {
const totalPages = Math.ceil(categories.count / queryParams.limit);

res.status(200).jsend.success({
categories: categories.rows,
metadata: {
totalCategories: categories.count,
currentPage: queryParams.page,
limit: queryParams.limit,
totalPages
}
});
})
.catch(() => res.status(500).jsend.error({
message: 'Your request cannot be completed at the moment. please try again.'
}));
},

singleCategory: (req, res) => {
const queryParams = req.paginationQueryParams;
const offset = ((queryParams.page - 1) * queryParams.limit);

if (Number.isNaN(Number(req.params.categoryId))) {
return res.status(400).jsend.fail({
message: 'Your must use a number as Id. please try again.'
});
}
Categories
.findById(Number(req.params.categoryId), {
include: [{
model: Articles,
as: 'articles',
offset,
limit: queryParams.limit,
include: [{
model: Users,
attributes: ['id', 'firstname', 'lastname', 'image', 'username']
}]
},
{
attributes: ['id'],
model: Articles,
as: 'count'
}
]
})
.then((category) => {
if (!category) {
return res.status(404).jsend.fail({
message: `Category with id ${req.params.categoryId} not found`
});
}
const totalPages = Math.ceil(category.count.length / queryParams.limit);

return res.status(200).jsend.success({
category: {
id: category.id,
name: category.name,
description: category.description,
poster: category.poster,
date: category.createdAt
},
articles: category.articles,
metadata: {
totalArticles: category.count.length,
currentPage: queryParams.page,
limit: queryParams.limit,
totalPages
}
});
})
.catch(() => res.status(500).jsend.error({
message: 'Your request cannot be completed at the moment. please try again.'
}));
},

createCategory: async (req, res) => {
let poster = null;
if (req.file) {
const file = dataUri(req);
// SAVES IMAGE TO CLOUDINARY
poster = await imageUpload(file, res, res.body);
poster = poster.url;
}

if (!req.body.name || !req.body.description) {
return res.status(400).jsend.fail({
message: 'Category must be created with a name and description'
});
}
Categories
.findOrCreate({
where: {
name: req.body.name
},
defaults: {
poster,
description: req.body.description
}
})
.spread(category => res.status(200).jsend.success({
message: 'Successfully added category',
category
}))
.catch(() => res.status(500).jsend.fail({
message: 'Something, went wrong. please try again '
}));
},

updateCategory: async (req, res) => {
if (Number.isNaN(Number(req.params.categoryId))) {
return res.status(400).jsend.fail({
message: 'Your must use a number as Id. please try again.'
});
}

let poster = null;
if (!req.body.name || !req.body.description) {
return res.status(400).jsend.fail({
message: 'Category must be updated with a name and description'
});
}
if (req.file) {
const file = dataUri(req);
// SAVES IMAGE TO CLOUDINARY
poster = await imageUpload(file, res, res.body);
poster = poster.url;
}
Categories
.update(
poster
? {
name: req.body.name,
description: req.body.description,
poster
}
: {
name: req.body.name,
description: req.body.description
}, {
where: {
id: req.params.categoryId
},
returning: true
}
)
.then((category) => {
if (category[0] === 0) {
return res.status(404).jsend.fail({
message: 'No category with this Id'
});
}
return res.status(200).jsend.success({
message: 'successfully updated category',
category: category[1][0]
});
})
.catch((err) => {
if (err.message === 'Validation error') {
return res.status(400).jsend.fail({
message: `Category with ${req.body.name} already exist`
});
}
return res.status(500).jsend.error({
message: 'Something, went wrong. please try again '
});
});
},

deleteCategory: (req, res) => {
if (Number.isNaN(Number(req.params.categoryId))) {
return res.status(400).jsend.fail({
message: 'Your must use a number as Id. please try again.'
});
}

Categories
.destroy({
where: {
id: req.params.categoryId
}
})
.then(() => res.status(202).jsend.success({
message: 'Category has been deleted'
}))
.catch(() => res.status(500).jsend.error({
message: 'Something, went wrong. please try again '
}));
}
};
export default categoryController;
6 changes: 4 additions & 2 deletions server/controllers/tagController.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import {
Op
} from 'sequelize';
import models from '../models';

const { Articles, Tags } = models;
Expand All @@ -13,7 +16,7 @@ const tagController = {
single: (req, res) => {
Tags
.findOne({
where: { name: req.params.tagName },
where: { name: { [Op.iLike]: `${req.params.tagName}` } },
include: [{
model: Articles,
as: 'articles',
Expand All @@ -31,6 +34,5 @@ const tagController = {
})
.catch(() => res.status(500).jsend.error({ message: 'Your request cannot be completed at the moment. please try again.' }));
}

};
export default tagController;
7 changes: 4 additions & 3 deletions server/middleware/paginationParamsValidations.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@


const paginationParamsValidations = (req, res, next) => {
// VALIDATE PAGE NUMBER
let page = parseInt(req.query.page, 10);
Expand All @@ -12,7 +10,10 @@ const paginationParamsValidations = (req, res, next) => {
limit = 10;
}

req.paginationQueryParams = { page, limit };
req.paginationQueryParams = {
page,
limit
};
next();
};

Expand Down
7 changes: 5 additions & 2 deletions server/migrations/20180830135814-create-users.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ module.exports = {
},
username: {
type: Sequelize.STRING,
allowNull: false
allowNull: false,
unique: true
},
firstname: {
type: Sequelize.STRING
Expand Down Expand Up @@ -54,6 +55,8 @@ module.exports = {
allowNull: false,
type: Sequelize.DATE
}
}, { freezeTableName: true }),
}, {
freezeTableName: true
}),
down: queryInterface => queryInterface.dropTable('Users')
};
11 changes: 10 additions & 1 deletion server/migrations/20180830140233-create-category.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,16 @@ module.exports = {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
type: Sequelize.STRING,
unique: true
},
poster: {
type: Sequelize.STRING,
allowNull: true
},
description: {
type: Sequelize.TEXT,
allowNull: false
},
poster: {
type: Sequelize.STRING,
Expand Down
Loading

0 comments on commit 6ba4c93

Please sign in to comment.