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 9, 2024
1 parent 608d497 commit ca8aaf5
Show file tree
Hide file tree
Showing 28 changed files with 1,184 additions and 120 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@ CLOUDINARY_SECRET=""
#USER ADMIN CREDENTIALS
ADMIN_PASSWORD=""
ADMIN_PHONE=""

# DOCKER CONFIGURATION
DEV_DB_HOST="db"
5 changes: 3 additions & 2 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 @@ -43,7 +44,7 @@
"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 missingFields = validateFields(req, ['name']);
if (missingFields.length) {
res.status(400).json({ ok: false, errorMessage: `Missing required fields: ${missingFields.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 missingFields = validateFields(req, ['name']);
if (missingFields.length) {
res.status(400).json({ ok: false, errorMessage: `Missing required fields: ${missingFields.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;
}
};
80 changes: 52 additions & 28 deletions src/controllers/roleControllers.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
import logger from '../logs/config';
import { Request, Response } from 'express';
import Role from '../database/models/role';
import Permission from '../database/models/permission';
import { sendInternalErrorResponse, validateFields } from '../validations';
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 { name, permissionIds } = req.body;
const missingFields = validateFields(req, ['name', 'permissionIds']);
if (missingFields.length > 0) {
res.status(400).json({ ok: false, message: `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 permissions = await Permission.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);
return;
}
};
const getAllRoles = async (req: Request, res: Response): Promise<void> => {
try {
const roles = await Role.findAll();
const roles = await Role.findAll({ include: { model: Permission, attributes: ['name'] } });
res.status(200).json({ ok: true, data: roles });
} catch (error) {
logger.error(error);
Expand All @@ -29,11 +48,10 @@ 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: Permission, attributes: ['name'] } });
if (!role) {
res.status(404).json({ ok: false, errorMessage: 'Roles can not be found' });
res.status(404).json({ ok: false, message: 'Roles can not be found' });
return;
}
res.status(200).json({ ok: true, data: role });
Expand All @@ -44,36 +62,42 @@ 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' });
res.status(404).json({ ok: false, message: 'Role not found' });
}
} catch (error) {
logger.error(error);
Expand Down
123 changes: 123 additions & 0 deletions src/controllers/vendorRequestController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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';
import User from '../database/models/user';
const fileUploadService = async (req: Request) => {
const store: string[] = [];
for (const file of req.files as Express.Multer.File[]) {
store.push(await uploadImage(file.buffer));
}
return store;
};
export const createVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
const vendorId = (await (req.user as any)).id;
const exisistingUser = await VendorRequest.findOne({ where: { vendorId } });
if (exisistingUser) {
logger.error('user request exists!');
res.status(400).json({ ok: false, message: 'this user has sent the request already!' });
return;
}
if (!req.files || +req.files.length < 6) {
res.status(400).json({ message: 'Please upload all required documents(6)' });
return;
}
if (req.body.agreement !== 'true') {
res.status(400).json({ message: 'Please agree to the terms and conditions' });
return;
}
const documents = await fileUploadService(req);

logger.info('documents uploaded successfully!');
const { agreement } = req.body;

const result = await VendorRequest.create({
vendorId,
agreement,
documents,
});
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 {
const request = await VendorRequest.findByPk(req.params.id);
if (!request) {
res.status(404).json({ ok: false, message: `No request found by id: ${req.params.id}` });
}
res.status(200).json({ ok: true, data: request });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const updateVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
if (req.body.agreement !== 'true') {
res.status(400).json({
ok: false,
errorMessage: `Please agree to our terms and conditions first`,
});
return;
}
const request = await VendorRequest.findByPk(req.params.id);
if (request === null) {
res.status(404).json({
ok: false,
message: 'request not found',
});
}
if (!req.files || +req.files.length !== 6) {
res.status(400).json({ ok: false, message: '6 files are required' });
return;
}
const docs = await fileUploadService(req);
request!.documents = docs;
req.body.agreement === 'true'
? (request!.agreement = req.body.agreement)
: (request!.agreement = request!.agreement);
await request?.save();

res.status(200).json({ ok: true, message: 'request updated successfully', data: request });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const deleteVendorRequest = async (req: Request, res: Response): Promise<void> => {
try {
const request = await VendorRequest.findByPk(req.params.id);
if (request === null) {
res.status(404).json({
ok: false,
message: 'request not found',
});
}
await request?.destroy();

res.status(200).json({ ok: true, message: 'Vendor request deleted successfully' });
} catch (error) {
logger.error(error);
sendInternalErrorResponse(res, error);
}
};
export const getAllVendorRequests = async (req: Request, res: Response): Promise<void> => {
try {
const requests = await VendorRequest.findAll({
include: {
model: User,
},
});

res.status(200).json({ ok: true, data: requests });
} 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
Loading

0 comments on commit ca8aaf5

Please sign in to comment.