Skip to content

Commit

Permalink
[finishes #187354254] permissions and role implementation
Browse files Browse the repository at this point in the history
finishes role and permission assigning functionality
	modified:   package.json
	new file:   src/controllers/permissionController.ts
	modified:   src/controllers/roleControllers.ts
	new file:   src/database/migrations/20240401004514-create-permissions-table.js
	modified:   src/database/migrations/20240413203456-create-role.js
	renamed:    src/database/migrations/20240425195548-create-user.js -> src/database/migrations/20240435195548-create-user.js
	new file:   src/database/migrations/20240505075748-role_permissions.js
	new file:   src/database/models/permission.ts
	modified:   src/database/models/role.ts
	new file:   src/database/models/rolePermissions.ts
	modified:   src/database/models/user.ts
	modified:   src/database/seeders/20240427082911-create-default-role.js
	new file:   src/database/seeders/20240501105101-permissions.js
	new file:   src/database/seeders/20240502080814-create-default-users.js
	new file:   src/database/seeders/20240502093711-role_permissions.js
	new file:   src/docs/permissions.yaml
	modified:   src/middlewares/authMiddlewares.ts
	modified:   src/routes/index.ts
	new file:   src/routes/permissionRoute.ts
	modified:   src/routes/roleRoute.ts
	new file:   src/test/permissionController.test.ts
  • Loading branch information
JeanIrad committed May 8, 2024
1 parent 608d497 commit 6e638e9
Show file tree
Hide file tree
Showing 24 changed files with 1,126 additions and 92 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
"prepare": "husky install && npx husky add .husky/pre-commit \"npx lint-staged\"",
"migrate": "npx sequelize-cli db:migrate",
"migrate:undo": "npx sequelize-cli db:migrate:undo",
"migrate:undo:all": "npx sequelize-cli db:migrate:undo:all",
"seed": "npx sequelize-cli db:seed:all",
"seed:undo": "npx sequelize-cli db:seed:undo"
"seed:undo": "npx sequelize-cli db:seed:undo:all"
},
"keywords": [],
"author": "",
Expand All @@ -42,8 +43,7 @@
"passport-jwt": "^4.0.1",
"pg": "^8.11.5",
"pg-hstore": "^2.3.4",
"randomstring": "^1.3.0",
"sequelize": "^6.37.2",
"sequelize": "^6.37.3",
"swagger-ui-express": "^5.0.0",
"uuid": "^9.0.1",
"winston": "^3.13.0",
Expand Down
83 changes: 83 additions & 0 deletions src/controllers/permissionController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Request, Response } from 'express';
import { sendInternalErrorResponse } from '../validations';
import Permission from '../database/models/permission';
import { validateFields } from '../validations/index';

// create a permission
export const createPermission = async (req: Request, res: Response): Promise<void> => {
try {
const validFields = validateFields(req, ['name']);
if (validFields.length) {
res.status(400).json({ ok: false, errorMessage: `Missing required fields: ${validFields.join(', ')}` });
return;
}
const createdPermission = await Permission.create({ name: req.body.name });
res.status(201).json({ ok: true, data: createdPermission });
} catch (error) {
sendInternalErrorResponse(res, error);
return;
}
};
// get all permissions
export const getAllPermissions = async (req: Request, res: Response): Promise<void> => {
try {
const permissions = await Permission.findAll({ attributes: ['id', 'name'] });
res.status(200).json({ ok: true, data: permissions });
} catch (error) {
sendInternalErrorResponse(res, error);
return;
}
};
// get a single permission
export const getSinglePermission = async (req: Request, res: Response): Promise<void> => {
try {
const permission = await Permission.findByPk(req.params.id);
if (!permission) {
res.status(404).json({ ok: false, errorMessage: 'Permission not found' });
return;
}
res.status(200).json({ ok: true, data: permission });
} catch (error) {
sendInternalErrorResponse(res, error);
return;
}
};
// update a permission
export const updatePermission = async (req: Request, res: Response): Promise<void> => {
try {
const validFields = validateFields(req, ['name']);
if (validFields.length) {
res.status(400).json({ ok: false, errorMessage: `Missing required fields: ${validFields.join(', ')}` });
return;
}
const permissionToUpdate = await Permission.findByPk(req.params.id);
if (!permissionToUpdate) {
res.status(404).json({ ok: false, errorMessage: 'Permission not found' });
return;
}
if (!req.body.name) res.status(400).json({ ok: false, errorMessage: 'Name is required' });

permissionToUpdate.name = req.body.name;

await permissionToUpdate.save();
res.status(200).json({ ok: true, data: permissionToUpdate });
} catch (error) {
sendInternalErrorResponse(res, error);
return;
}
};
// delete a permission
export const deletePermission = async (req: Request, res: Response): Promise<void> => {
try {
const permissionToDelete = await Permission.findByPk(req.params.id);
if (!permissionToDelete) {
res.status(404).json({ ok: false, errorMessage: 'Permission not found' });
return;
}
await permissionToDelete.destroy();
res.status(200).json({ ok: true, message: 'permission deleted successfully!' });
} catch (error) {
sendInternalErrorResponse(res, error);
return;
}
};
78 changes: 53 additions & 25 deletions src/controllers/roleControllers.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import logger from '../logs/config';
import { Request, Response } from 'express';
import Role from '../database/models/role';
import Permision from '../database/models/permission';
import { sendInternalErrorResponse, validateFields } from '../validations';
// import User from '../database/models/user';
import sequelize from '../database/models/index';

const createRole = async (req: Request, res: Response): Promise<void> => {
try {
if (validateFields(req, ['name']).length !== 0) {
res.status(400).json({ ok: false, errorMessage: 'Role name is required' });
const missingFields = validateFields(req, ['name', 'permissionIds']);
if (missingFields.length !== 0) {
res.status(400).json({ ok: false, errorMessage: `required fields: ${missingFields.join(', ')}` });
return;
}
const { name, displayName } = req.body;
const createdRole = await Role.create({ name, displayName });
res.status(201).json({ ok: true, data: createdRole });
const { name, permissionIds } = req.body;
const permissions = await Permision.findAll({ where: { id: permissionIds } });
if (permissions.length !== permissionIds.length) {
logger.error(`Adding role: One or more permissions not found`);
res.status(404).json({
ok: false,
message: 'roles create permissions Not Found',
});
return;
}
const transaction = await sequelize.transaction();
try {
const role = await Role.create({ name }, { transaction });
await (role as any).addPermissions(permissions, { transaction });
await transaction.commit();

res.status(201).json({ ok: true, message: 'role created successfully' });
} catch (err) {
logger.error('error creating role');
await transaction.rollback();
throw err;
}
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
Expand All @@ -20,7 +43,7 @@ const createRole = async (req: Request, res: Response): Promise<void> => {
};
const getAllRoles = async (req: Request, res: Response): Promise<void> => {
try {
const roles = await Role.findAll();
const roles = await Role.findAll({ include: { model: Permision, attributes: ['name'] } });
res.status(200).json({ ok: true, data: roles });
} catch (error) {
logger.error(error);
Expand All @@ -29,9 +52,8 @@ const getAllRoles = async (req: Request, res: Response): Promise<void> => {
}
};
const getSingleRole = async (req: Request, res: Response): Promise<void> => {
const { id } = req.params;
try {
const role = await Role.findByPk(id);
const role = await Role.findByPk(req.params.id, { include: { model: Permision, attributes: ['name'] } });
if (!role) {
res.status(404).json({ ok: false, errorMessage: 'Roles can not be found' });
return;
Expand All @@ -44,34 +66,40 @@ const getSingleRole = async (req: Request, res: Response): Promise<void> => {
}
};
const updateRole = async (req: Request, res: Response): Promise<void> => {
const { id } = req.params;
const contentsToUpdate = { ...req.body };
try {
const roleToupdate = await Role.findByPk(id);
if (!roleToupdate) {
res.status(404).json({ ok: false, errorMessage: 'Role not found' });
return;
}
if (contentsToUpdate.name) {
roleToupdate.name = contentsToUpdate.name;
}
if (contentsToUpdate.displayName) {
roleToupdate.displayName = contentsToUpdate.displayName;
const transaction = await sequelize.transaction();
try {
const role = await Role.findByPk(req.params.id, { transaction });
if (!role) {
logger.error(`Role not found`);
res.status(404).json({ ok: false, errorMessage: 'Role not found' });
return;
}
const missingFields = validateFields(req, ['name', 'permissionIds']);
if (missingFields.length > 0) {
res.status(400).json({ ok: false, errorMessage: `the required fields: ${missingFields.join(', ')}` });
}
role.name = req.body.name;
const updatedRole = await role.save({ transaction });
await (updatedRole as any).setPermissions(req.body.permissionIds, { transaction });
await transaction.commit();
res.status(200).json({ ok: true, message: 'role updated successfully' });
} catch (err) {
logger.error('error updating role');
await transaction.rollback();
throw err;
}
await roleToupdate.save();
res.status(200).json({ ok: true, data: roleToupdate });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
return;
}
};
const deleteRole = async (req: Request, res: Response): Promise<void> => {
const { id } = req.params;
try {
const deletedCount = await Role.destroy({ where: { id } });
const deletedCount = await Role.destroy({ where: { id: req.params.id } });
if (deletedCount === 1) {
res.status(200).json({ ok: true, data: deletedCount });
res.status(200).json({ ok: true, message: 'Role deleted successfully' });
} else {
res.status(404).json({ ok: false, errorMessage: 'Role not found' });
}
Expand Down
76 changes: 76 additions & 0 deletions src/controllers/vendorRequestController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Request, Response } from 'express';
import { sendInternalErrorResponse } from '../validations';
import logger from '../logs/config';
import VendorRequest from '../database/models/vendorRequest';
import uploadImage from '../helpers/claudinary';
import { validateFields } from '../validations';

export const createVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
if (!req.files || +req.files.length < 6) {
res.status(400).json({ message: 'Please upload all required documents(6)' });
return;
}

const missingFields = validateFields(req, ['vendorId', 'agreement']);
if (missingFields.length > 0) {
res.status(400).json({ message: `Please fill in all required fields: ${missingFields.join(', ')}` });
return;
}
if (!req.body.agreement) {
res.status(400).json({ message: 'Please agree to the terms and conditions' });
return;
}
const documents = [];
for (const file of req.files as Express.Multer.File[]) {
documents.push(await uploadImage(file.buffer));
}
const { vendorId, agreement, status } = req.body;
const result = await VendorRequest.create({ vendorId, agreement, documents, status });
console.log(result);
res.status(201).json({ ok: true, data: result });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const getVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment

res.status(200).json({ message: 'Vendor request retrieved successfully' });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const updateVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment

res.status(200).json({ message: 'Vendor request updated successfully' });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const deleteVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment

res.status(200).json({ message: 'Vendor request deleted successfully' });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const getAllVendorRequests = async (req: Request, res: Response): Promise<void> => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment

res.status(200).json({ message: 'All vendor requests retrieved successfully' });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
16 changes: 7 additions & 9 deletions src/database/migrations/20240413203456-create-role.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Roles', {
id: {
allowNull: false,
type: Sequelize.INTEGER,
primaryKey: true,
type: Sequelize.UUID,
defaultValue: Sequelize.UUIDV4,
unique: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
type: Sequelize.ENUM('admin', 'buyer', 'guest', 'vendor'),
allowNull: false,
unique: true,
set(value) {
this.setDataValue('name', value.toLowerCase());
},
},
displayName: {
type: Sequelize.STRING,
allowNull: true,
},

createdAt: {
allowNull: false,
type: Sequelize.DATE,
Expand Down
2 changes: 1 addition & 1 deletion src/database/migrations/20240425195548-create-user.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ module.exports = {
defaultValue: 'active',
},
RoleId: {
type: Sequelize.UUID,
type: Sequelize.INTEGER,
allowNull: false,
references: {
model: 'Roles',
Expand Down
32 changes: 32 additions & 0 deletions src/database/migrations/20240501104514-create-permissions-table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';

/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable('Permissions', {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
},
name: {
type: Sequelize.STRING,
allowNull: false,
defaultValue: 'read',
unique: true,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},

async down(queryInterface, Sequelize) {
await queryInterface.dropTable('Permissions');
},
};
Loading

0 comments on commit 6e638e9

Please sign in to comment.