Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Implement user profile update feature #64

Merged
merged 2 commits into from
May 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default {
verbose: true,
forceExit: true,
clearMocks: true,
testTimeout: 30000,
resetMocks: true,
restoreMocks: true,
collectCoverageFrom: [
Expand Down
6 changes: 5 additions & 1 deletion src/__test__/isAllowed.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,12 @@
const connection = getConnection();
const userRepository = connection.getRepository(User);


Check warning on line 53 in src/__test__/isAllowed.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Trailing spaces not allowed

Check warning on line 53 in src/__test__/isAllowed.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Trailing spaces not allowed
// Delete all records from the User
await userRepository.delete({});

// Close the connection to the test database
await connection.close();
await connection.close();
});

describe('Middleware - checkUserStatus', () => {
Expand All @@ -63,7 +67,7 @@
};
nextMock = jest.fn();
});

Check warning on line 70 in src/__test__/isAllowed.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Trailing spaces not allowed

Check warning on line 70 in src/__test__/isAllowed.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Trailing spaces not allowed
it('should return 401 if user is not authenticated', async () => {
await checkUserStatus(reqMock as Request, resMock as Response, nextMock);
expect(responseError).toHaveBeenCalledWith(resMock, 401, 'Authentication required');
Expand All @@ -82,7 +86,7 @@
await checkUserStatus(reqMock as Request, resMock as Response, nextMock);
expect(nextMock).toHaveBeenCalled();
});

Check warning on line 89 in src/__test__/isAllowed.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Trailing spaces not allowed

Check warning on line 89 in src/__test__/isAllowed.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Trailing spaces not allowed
it('should return 403 if user status is suspended', async () => {
reqMock = { user: { id: suspendedUserId } };
await checkUserStatus(reqMock as Request, resMock as Response, nextMock);
Expand Down
10 changes: 9 additions & 1 deletion src/__test__/logout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,15 @@ beforeAll(async () => {
});

afterAll(async () => {
await getConnection('testConnection').close();
const connection = getConnection('testConnection');
const userRepository = connection.getRepository(User);

// Delete all records from the User
await userRepository.delete({});

// Close the connection to the test database
await connection.close();

server.close();
});

Expand Down
2 changes: 1 addition & 1 deletion src/__test__/roleCheck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ afterAll(async () => {
const userRepository = connection.getRepository(User);

// Delete all records from the User
await userRepository.clear();
await userRepository.delete({});

// Close the connection to the test database
await connection.close();
Expand Down
47 changes: 46 additions & 1 deletion src/__test__/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,25 @@
import { app, server } from '../index';
import { createConnection, getConnection, getConnectionOptions, getRepository } from 'typeorm';
import { User } from '../entities/User';
import { response } from 'express';

Check warning on line 5 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'response' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 5 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'response' is defined but never used

Check warning on line 5 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'response' is defined but never used. Allowed unused vars must match /^_/u

Check warning on line 5 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'response' is defined but never used

beforeAll(async () => {
// Connect to the test database
const connectionOptions = await getConnectionOptions();

await createConnection({ ...connectionOptions, name: 'testConnection' });

});



jest.setTimeout(20000);
afterAll(async () => {
const connection = getConnection('testConnection');
const userRepository = connection.getRepository(User);

// Delete all records from the User
await userRepository.clear();
await userRepository.delete({});

// Close the connection to the test database
await connection.close();
Expand Down Expand Up @@ -81,7 +86,7 @@
};

// Create a new user
const res = await request(app).post('/user/register').send(newUser);

Check warning on line 89 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 89 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used

Check warning on line 89 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 89 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used

const userRepository = getRepository(User);
const user = await userRepository.findOne({ where: { email: newUser.email } });
Expand Down Expand Up @@ -194,3 +199,43 @@
}
});
});
describe('PUT/user/update', () =>{
it('should return 401 if user is not authenticated', async() =>{

Check warning on line 203 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Missing space before function parentheses

Check warning on line 203 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

Missing space before function parentheses
const newUser = {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
password: 'password',
gender: 'Male',
phoneNumber: '12345678900',
userType: 'Buyer',
photoUrl: 'https://example.com/photo.jpg',
};

// Create a new user
const res = await request(app).post('/user/register').send(newUser);

Check warning on line 216 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 216 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used

Check warning on line 216 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 216 in src/__test__/route.test.ts

View workflow job for this annotation

GitHub Actions / build-lint-test-coverage

'res' is assigned a value but never used
const userRepository = getRepository(User);

const user = await userRepository.findOne({ where: { email: newUser.email } });
if(user){
const updateUser = {
id: user.id,
firstName: "Biguseers2399",
lastName: "1",
email: "[email protected]",
gender: "Male",
phoneNumber: "0790easdas7dsdfd76175",
photoUrl: "photo",
}
const res = await request(app).put('/user/update').send(updateUser)
expect(res.status).toBe(201);
expect(res.body).toEqual({
status: 'success',
data: {
code: 201,
message: 'User Profile has successfully been updated',
},
});
}
});
});
2 changes: 1 addition & 1 deletion src/__test__/userServices.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ afterAll(async () => {
const userRepository = connection.getRepository(User);

// Delete all records from the User
await userRepository.clear();
await userRepository.delete({});

// Close the connection to the test database
await connection.close();
Expand Down
4 changes: 4 additions & 0 deletions src/__test__/userStatus.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ afterAll(async () => {
const connection = getConnection();
const userRepository = connection.getRepository(User);


// Delete all records from the User
await userRepository.delete({});

// Close the connection to the test database
await connection.close();
server.close();
Expand Down
5 changes: 5 additions & 0 deletions src/controllers/authController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import {
userValidateOTP,
userResendOtpService,
logoutService,

} from '../services';
import { userPasswordResetService } from '../services/userServices/userPasswordResetService';
import { sendPasswordResetLinkService } from '../services/userServices/sendResetPasswordLinkService';
import { activateUserService } from '../services/updateUserStatus/activateUserService';
import { deactivateUserService } from '../services/updateUserStatus/deactivateUserService';
import {userProfileUpdateServices} from '../services/userServices/userProfileUpdateServices';

export const userRegistration = async (req: Request, res: Response) => {
await userRegistrationService(req, res);
Expand Down Expand Up @@ -63,3 +65,6 @@ export async function disactivateUser(req: Request, res: Response) {
export const logout = async (req: Request, res: Response) => {
await logoutService(req, res);
};
export const userProfileUpdate = async(req: Request, res: Response) =>{
await userProfileUpdateServices(req,res);
}
17 changes: 17 additions & 0 deletions src/entities/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,23 @@ export interface UserInterface {
updatedAt: Date;
}

export interface UserInterface {
id: string;
firstName: string;
lastName: string;
email: string;
password: string;
gender: string;
phoneNumber: string;
photoUrl?: string;
verified: boolean;
status: 'active' | 'suspended';
userType: 'Admin' | 'Buyer' | 'Vendor';
role: string;
createdAt: Date;
updatedAt: Date;
}

@Entity()
@Unique(['email'])
export class User {
Expand Down
15 changes: 9 additions & 6 deletions src/routes/UserRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ import {
logout,
} from '../controllers';

import { activateUser, disactivateUser } from '../controllers/index';
import { hasRole } from '../middlewares/roleCheck';


import { activateUser,disactivateUser , userProfileUpdate} from '../controllers/index';
import {hasRole} from '../middlewares/roleCheck';
import { isTokenValide } from '../middlewares/isValid';

const router = Router();
Expand All @@ -26,9 +28,10 @@ router.post('/enable-2fa', enable2FA);
router.post('/disable-2fa', disable2FA);
router.post('/verify-otp', verifyOTP);
router.post('/resend-otp', resendOTP);
router.post('/activate', isTokenValide, hasRole('ADMIN'), activateUser);
router.post('/deactivate', isTokenValide, hasRole('ADMIN'), disactivateUser);
router.post('/password/reset', userPasswordReset);
router.post('/password/reset/link', sendPasswordResetLink);
router.post('/activate',isTokenValide,hasRole("ADMIN"),activateUser);
router.post('/deactivate',isTokenValide,hasRole("ADMIN"),disactivateUser);
router.post("/password/reset", userPasswordReset);
router.post("/password/reset/link", sendPasswordResetLink);
router.put('/update', userProfileUpdate);

export default router;
2 changes: 2 additions & 0 deletions src/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ export * from './userServices/userValidateOTP';
export * from './userServices/userLoginService';
export * from './userServices/userResendOTP';
export * from './userServices/logoutServices';

export * from './userServices/userProfileUpdateServices';
53 changes: 53 additions & 0 deletions src/services/userServices/userProfileUpdateServices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Request, Response } from "express";
import { responseError, responseSuccess } from "../../utils/response.utils";
import { User, UserInterface } from "../../entities/User";
import { getRepository } from "typeorm";
import { userProfileUpdate } from "../../controllers/authController";

declare module "express"{
interface Request{
user?: Partial <UserInterface>
}
}


export const userProfileUpdateServices = async (req: Request, res: Response) =>{
try {
if(!req.body){
return responseError(res, 401, "body required")
}

const { firstName, lastName, gender, phoneNumber, photoUrl, email, id} = req.body;

// Validate user input
if (!firstName.trim() && !lastName.trim() && !gender.trim() && !phoneNumber.trim() && !photoUrl.trim() && !email.trim() && !id.trim()) {
return responseError(res, 400, 'Fill all the field');
}


const userRepository = getRepository(User)
const existingUser = await userRepository.findOne({
where: {email: req.body.email}
})

if (!existingUser){
return responseError(res, 401, "User not found")
}
if (existingUser.id !== id) {
return responseError(res, 403, "You are not authorized to edit this profile.");
}

existingUser.firstName = firstName
existingUser.lastName = lastName
existingUser.gender = gender
existingUser.phoneNumber = phoneNumber
existingUser.photoUrl = photoUrl

await userRepository.save(existingUser);
return responseSuccess(res, 201, "User Profile has successfully been updated");
} catch (error) {
responseError(res, 400, (error as Error).message)

}

}
2 changes: 1 addition & 1 deletion src/utils/sendStatusMail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ export const sendEmail = async (type: string, data: IData) => {
console.error('Email or password for mail server not configured');
return
}
};
};