Skip to content

Commit

Permalink
feat(profile-pic): implemented change profile picture
Browse files Browse the repository at this point in the history
  • Loading branch information
King-Mikaelson committed Aug 12, 2024
1 parent f19608e commit 7dafe78
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/helpers/SystemMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const PROFILE_PIC_NOT_FOUND = 'Previous profile picture pic not found';
export const ERROR_DIRECTORY = 'Error creating uploads directory:';
export const DIRECTORY_CREATED = 'Uploads directory created at:';
export const PICTURE_UPDATED = 'Profile picture updated successfully';
export const FILE_SAVE_ERROR = 'Error saving file to disk';
export const FILE_EXCEEDS_SIZE = resource => {
return `File size exceeds ${resource} MB limit`

Expand Down
3 changes: 2 additions & 1 deletion src/helpers/app-constants.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';

export const MAX_PROFILE_PICTURE_SIZE = 2 * 1024 * 1024;
export const VALID_UPLOADS_MIME_TYPES = ['image/jpeg', 'image/png'];
export const BASE_URL = "https://staging.api-nestjs.boilerplate.hng.tech";
export const PROFILE_PHOTO_UPLOADS = '/uploads';
export const PROFILE_PHOTO_UPLOADS = path.join(__dirname, '..', '..', 'uploads')
1 change: 0 additions & 1 deletion src/modules/profile/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import { UpdateProfileDto } from './dto/update-profile.dto';
import {
BASE_URL,
MAX_PROFILE_PICTURE_SIZE,
PROFILE_PHOTO_UPLOADS,
VALID_UPLOADS_MIME_TYPES,
} from '../../helpers/app-constants';

Expand Down
14 changes: 13 additions & 1 deletion src/modules/profile/profile.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import * as path from 'path';
import { CustomHttpException } from '../../helpers/custom-http-filter';
import * as SYS_MSG from '../../helpers/SystemMessages';
import { UploadProfilePicDto } from './dto/upload-profile-pic.dto';
import { PROFILE_PHOTO_UPLOADS } from '../../helpers/app-constants';

@Injectable()
export class ProfileService {
Expand All @@ -25,7 +26,7 @@ export class ProfileService {
@InjectRepository(Profile) private profileRepository: Repository<Profile>,
@InjectRepository(User) private userRepository: Repository<User>
) {
this.uploadsDir = path.join(__dirname, '..', '..', 'uploads');
this.uploadsDir = PROFILE_PHOTO_UPLOADS;
this.createUploadsDirectory().catch(error => {
console.error('Failed to create uploads directory:', error);
});
Expand Down Expand Up @@ -149,10 +150,21 @@ export class ProfileService {
}
}


const fileExtension = path.extname(uploadProfilePicDto.file.originalname);
const fileName = `${userId}${fileExtension}`;
const filePath = path.join(this.uploadsDir, fileName);

const writeStream = await fs.open(filePath, 'w');
try {
await writeStream.writeFile(uploadProfilePicDto.file.buffer);
await writeStream.close();
} catch (error) {
await writeStream.close();
throw new CustomHttpException(SYS_MSG.FILE_SAVE_ERROR, HttpStatus.INTERNAL_SERVER_ERROR);
}


await sharp(uploadProfilePicDto.file.buffer).resize({ width: 200, height: 200 }).toFile(filePath);

profile.profile_pic_url = `${baseUrl}/uploads/${fileName}`;
Expand Down
12 changes: 12 additions & 0 deletions src/modules/profile/tests/profile.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { User } from '../../user/entities/user.entity';
import { UpdateProfileDto } from '../dto/update-profile.dto';
import * as path from 'path';
import * as fs from 'fs/promises';
import { FileHandle } from 'fs/promises';
import * as sharp from 'sharp';
import { CustomHttpException } from '../../../helpers/custom-http-filter';
import { PICTURE_UPDATED } from '../../../helpers/SystemMessages';
Expand Down Expand Up @@ -192,6 +193,10 @@ describe('ProfileService', () => {
originalname: 'test.jpg',
};
const mockUploadProfilePicDto = { file: mockFile as any };
const mockFileHandle = {
writeFile: jest.fn().mockResolvedValue(undefined),
close: jest.fn().mockResolvedValue(undefined),
} as unknown as FileHandle;

it('should throw an exception if no file is provided', async () => {
await expect(service.uploadProfilePicture(userId, { file: null }, baseUrl)).rejects.toThrow(CustomHttpException);
Expand All @@ -217,6 +222,8 @@ describe('ProfileService', () => {
});

it('should delete previous profile picture if it exists', async () => {
jest.spyOn(fs, 'open').mockResolvedValue(mockFileHandle);

jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser);

jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser);
Expand All @@ -238,6 +245,8 @@ describe('ProfileService', () => {
});

it('should handle non-existent previous profile picture', async () => {
jest.spyOn(fs, 'open').mockResolvedValue(mockFileHandle);

const mockResult: UpdateResult = {
generatedMaps: [],
raw: [],
Expand All @@ -258,6 +267,9 @@ describe('ProfileService', () => {
});

it('should save new profile picture and update profile', async () => {

jest.spyOn(fs, 'open').mockResolvedValue(mockFileHandle);

jest.spyOn(userRepository, 'findOne').mockResolvedValue(mockUser);

(sharp as jest.MockedFunction<typeof sharp>).mockReturnValue({
Expand Down

0 comments on commit 7dafe78

Please sign in to comment.