diff --git a/brints-estate-api/src/users/dto/update-user.dto.ts b/brints-estate-api/src/users/dto/update-user.dto.ts index 6210e57..6d3c961 100644 --- a/brints-estate-api/src/users/dto/update-user.dto.ts +++ b/brints-estate-api/src/users/dto/update-user.dto.ts @@ -1,4 +1,14 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreateUserDto } from '../../auth/dto/create-user.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; -export class UpdateUserDto extends PartialType(CreateUserDto) {} +export class UpdateUserDto extends PartialType(CreateUserDto) { + @ApiProperty({ + description: 'The id of the user', + example: '60b7f3a8d8e9a7e4d8f9b1a7', + }) + @IsNotEmpty() + @IsString() + id: string; +} diff --git a/brints-estate-api/src/users/providers/update-user.provider.ts b/brints-estate-api/src/users/providers/update-user.provider.ts index e63da8a..bab903e 100644 --- a/brints-estate-api/src/users/providers/update-user.provider.ts +++ b/brints-estate-api/src/users/providers/update-user.provider.ts @@ -1,4 +1,63 @@ -import { Injectable } from '@nestjs/common'; +import { HttpStatus, Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { User } from '../entities/user.entity'; +import { Repository } from 'typeorm'; +import { UpdateUserDto } from '../dto/update-user.dto'; +import { CustomException } from 'src/exceptions/custom.exception'; +import { UploadToAwsProvider } from 'src/uploads/providers/upload-to-aws.provider'; +import { IActiveUser } from 'src/auth/interfaces/active-user.interface'; @Injectable() -export class UpdateUserProvider {} +export class UpdateUserProvider { + constructor( + @InjectRepository(User) + private readonly userRepository: Repository, + + private uploadToAwsProvider: UploadToAwsProvider, + ) {} + + public async update( + updateUserDto: UpdateUserDto, + activeUser: IActiveUser, + file: Express.Multer.File, + ): Promise { + const user = await this.userRepository.findOneBy({ id: updateUserDto.id }); + + if (!user) + throw new CustomException(HttpStatus.NOT_FOUND, 'User not found'); + + if (user.id !== activeUser.sub) + throw new CustomException( + HttpStatus.FORBIDDEN, + 'You are not allowed to perform this action', + ); + + if (file) { + const fileUrl = await this.uploadToAwsProvider.fileUpload(file); + user.image_url = fileUrl; + //await this.uploadToAwsProvider.deleteFile(user.image_url); + await this.userRepository.update(user.id, { image_url: fileUrl }); + } + + if (updateUserDto.phone_number) { + const phoneNumberExists = await this.userRepository.findOneBy({ + phone_number: updateUserDto.phone_number, + }); + if (phoneNumberExists) + throw new CustomException( + HttpStatus.CONFLICT, + 'Phone number already in use.', + ); + } + + user.first_name = updateUserDto.first_name ?? user.first_name; + user.last_name = updateUserDto.last_name ?? user.last_name; + user.phone_number = updateUserDto.phone_number ?? user.phone_number; + user.gender = updateUserDto.gender ?? user.gender; + user.marketing = updateUserDto.marketing ?? user.marketing; + + await this.userRepository.update(user.id, user); + + return user; + } +} diff --git a/brints-estate-api/src/users/providers/users.service.spec.ts b/brints-estate-api/src/users/providers/users.service.spec.ts index 296d5c5..19c7ff7 100644 --- a/brints-estate-api/src/users/providers/users.service.spec.ts +++ b/brints-estate-api/src/users/providers/users.service.spec.ts @@ -9,6 +9,7 @@ import { GetUserProfileProvider } from './get-user-profile.provider'; import { ResetPasswordProvider } from './reset-password.provider'; import { ChangePasswordProvider } from './change-password.provider'; import { ForgotPasswordProvider } from './forgot-password.provider'; +import { UpdateUserProvider } from './update-user.provider'; describe('UsersService', () => { let service: UsersService; @@ -30,6 +31,7 @@ describe('UsersService', () => { { provide: ResetPasswordProvider, useValue: {} }, { provide: ChangePasswordProvider, useValue: {} }, { provide: ForgotPasswordProvider, useValue: {} }, + { provide: UpdateUserProvider, useValue: {} }, ], }).compile(); diff --git a/brints-estate-api/src/users/providers/users.service.ts b/brints-estate-api/src/users/providers/users.service.ts index 5f3bd31..4024b1c 100644 --- a/brints-estate-api/src/users/providers/users.service.ts +++ b/brints-estate-api/src/users/providers/users.service.ts @@ -18,6 +18,8 @@ import { User } from '../entities/user.entity'; import { ForgotPasswordProvider } from './forgot-password.provider'; import { ForgotPasswordDto } from '../dto/forgot-password.dto'; import { IResetPassword } from '../interface/reset-password.interface'; +import { UpdateUserProvider } from './update-user.provider'; +import { UpdateUserDto } from '../dto/update-user.dto'; @Injectable() export class UsersService { @@ -37,6 +39,8 @@ export class UsersService { private readonly changePasswordProvider: ChangePasswordProvider, private readonly forgotPasswordProvider: ForgotPasswordProvider, + + private readonly updateUserProvider: UpdateUserProvider, ) {} public async verifyUserEmail(verifyEmailDto: VerifyEmailDto) { @@ -90,4 +94,12 @@ export class UsersService { ): Promise { return this.forgotPasswordProvider.forgotPassword(forgotPasswordDto); } + + public async updateUser( + updateUserDto: UpdateUserDto, + activeUser: IActiveUser, + file: Express.Multer.File, + ): Promise { + return this.updateUserProvider.update(updateUserDto, activeUser, file); + } } diff --git a/brints-estate-api/src/users/users.controller.ts b/brints-estate-api/src/users/users.controller.ts index 22a11dd..4359858 100644 --- a/brints-estate-api/src/users/users.controller.ts +++ b/brints-estate-api/src/users/users.controller.ts @@ -10,8 +10,9 @@ import { Body, HttpCode, Param, + Put, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiResponse, ApiTags } from '@nestjs/swagger'; import { UsersService } from './providers/users.service'; import { VerifyEmailDto } from './dto/verify-email.dto'; import { VerifyPhoneNumberDto } from './dto/verify-phone-number.dto'; @@ -26,6 +27,7 @@ import { ResetPasswordDto } from './dto/reset-password.dto'; import { ChangePasswordDto } from './dto/change-password.dto'; import { ForgotPasswordDto } from './dto/forgot-password.dto'; import { IResetPassword } from './interface/reset-password.interface'; +import { UpdateUserDto } from './dto/update-user.dto'; @Controller('user') @ApiTags('Users') @@ -169,4 +171,31 @@ export class UsersController { payload, }; } + + @ApiResponse({ + status: HttpStatus.OK, + description: 'User profile updated successfully', + }) + @Put('update') + @Auth(AuthType.Bearer) + @HttpCode(HttpStatus.OK) + @UseInterceptors(ClassSerializerInterceptor) + @UseFilters(HttpExceptionFilter) + public async updateUser( + @Body() updateUserDto: UpdateUserDto, + @ActiveUser() activeUser: IActiveUser, + file: Express.Multer.File, + ) { + const payload = await this.usersService.updateUser( + updateUserDto, + activeUser, + file, + ); + + return { + message: 'Profile updated successfully', + status_code: HttpStatus.OK, + payload, + }; + } } diff --git a/brints-estate-api/src/users/users.module.ts b/brints-estate-api/src/users/users.module.ts index 1eb2621..e4a9f67 100644 --- a/brints-estate-api/src/users/users.module.ts +++ b/brints-estate-api/src/users/users.module.ts @@ -27,6 +27,8 @@ import { SendVerificationTokenEmailProvider } from 'src/services/email-service/m import { SendOtpProvider } from 'src/services/email-service/mailgun-service/providers/send-otp.provider'; import { SendResetPasswordConfirmationProvider } from 'src/services/email-service/mailgun-service/providers/send-reset-password-confirmation.provider'; import { UpdateUserProvider } from './providers/update-user.provider'; +import { UploadToAwsProvider } from 'src/uploads/providers/upload-to-aws.provider'; +import { AppConfigService } from 'src/config/config.service'; @Module({ controllers: [UsersController], @@ -54,6 +56,8 @@ import { UpdateUserProvider } from './providers/update-user.provider'; SendOtpProvider, SendResetPasswordConfirmationProvider, UpdateUserProvider, + UploadToAwsProvider, + AppConfigService, ], imports: [ TypeOrmModule.forFeature([User, UserAuth]),