Skip to content

Commit

Permalink
Merge pull request #789 from hngprojects/dev
Browse files Browse the repository at this point in the history
Merge dev branch into staging
  • Loading branch information
Homoakin619 authored Aug 14, 2024
2 parents 1011043 + 2ae7e97 commit c05ee85
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 10 deletions.
24 changes: 23 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@nestjs/typeorm": "^10.0.2",
"@types/nodemailer": "^6.4.15",
"@types/speakeasy": "^2.0.10",
"@vitalets/google-translate-api": "^9.2.0",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"bull": "^4.16.0",
Expand Down
19 changes: 16 additions & 3 deletions src/modules/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,20 @@ import {
ApiUnauthorizedResponse,
} from '@nestjs/swagger';
import * as SYS_MSG from '../../helpers/SystemMessages';
import { Body, Controller, HttpCode, HttpStatus, Post, Req, Request, Res, UseGuards, Get, Patch } from '@nestjs/common';
import {
Body,
Controller,
HttpCode,
HttpStatus,
Post,
Req,
Request,
Res,
UseGuards,
Get,
Patch,
Query,
} from '@nestjs/common';
import { CreateUserDTO } from './dto/create-user.dto';
import { skipAuth } from '../../helpers/skipAuth';
import AuthenticationService from './auth.service';
Expand Down Expand Up @@ -106,8 +119,8 @@ export default class RegistrationController {
@ApiResponse({ status: 200, description: 'Verify Payload sent from google', type: AuthResponseDto })
@ApiBadRequestResponse({ description: 'Invalid Google token' })
@HttpCode(200)
async googleAuth(@Body() body: GoogleAuthPayload) {
return this.authService.googleAuth(body);
async googleAuth(@Body() body: GoogleAuthPayload, @Query('mobile') isMobile: string) {
return this.authService.googleAuth({ googleAuthPayload: body, isMobile });
}

@skipAuth()
Expand Down
12 changes: 10 additions & 2 deletions src/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,17 @@ export default class AuthenticationService {
};
}

async googleAuth(googleAuthPayload: GoogleAuthPayload) {
async googleAuth({ googleAuthPayload, isMobile }: { googleAuthPayload: GoogleAuthPayload; isMobile: string }) {
const idToken = googleAuthPayload.id_token;
const verifyTokenResponse: GoogleVerificationPayloadInterface = await this.googleAuthService.verifyToken(idToken);
let verifyTokenResponse: GoogleVerificationPayloadInterface;

if (isMobile === 'true') {
const request = await fetch(`https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${idToken}`);
verifyTokenResponse = await request.json();
} else {
verifyTokenResponse = await this.googleAuthService.verifyToken(idToken);
}

const userEmail = verifyTokenResponse.email;
const userExists = await this.userService.getUserRecord({ identifier: userEmail, identifierType: 'email' });

Expand Down
2 changes: 1 addition & 1 deletion src/modules/help-center/help-center.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,4 @@ export class HelpCenterController {
}
}
}
}
}
30 changes: 27 additions & 3 deletions src/modules/help-center/help-center.service.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HttpStatus, Injectable, NotFoundException } from '@nestjs/common';
import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { HelpCenterEntity } from '../help-center/entities/help-center.entity';
Expand Down Expand Up @@ -88,16 +88,40 @@ export class HelpCenterService {
}

async updateTopic(id: string, updateHelpCenterDto: UpdateHelpCenterDto) {
const existingTopic = await this.helpCenterRepository.findOneBy({ id });
if (!existingTopic) {
throw new HttpException(
{
status: 'error',
message: 'Topic not found, please check and try again',
status_code: HttpStatus.NOT_FOUND,
},
HttpStatus.NOT_FOUND
);
}

await this.helpCenterRepository.update(id, updateHelpCenterDto);
const query = await this.helpCenterRepository.findOneBy({ id });
const updatedTopic = await this.helpCenterRepository.findOneBy({ id });

return {
status_code: HttpStatus.OK,
message: REQUEST_SUCCESSFUL,
data: query,
data: updatedTopic,
};
}

async removeTopic(id: string): Promise<void> {
const existingTopic = await this.helpCenterRepository.findOneBy({ id });
if (!existingTopic) {
throw new HttpException(
{
status: 'error',
message: 'Topic not found, unable to delete',
status_code: HttpStatus.NOT_FOUND,
},
HttpStatus.NOT_FOUND
);
}
await this.helpCenterRepository.delete(id);
}
}
14 changes: 14 additions & 0 deletions src/modules/testimonials/dto/update-testimonial.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { IsString, IsOptional } from 'class-validator';
import { ApiPropertyOptional } from '@nestjs/swagger';

export class UpdateTestimonialDto {
@ApiPropertyOptional({ description: 'Updated content of the testimonial' })
@IsString()
@IsOptional()
content?: string;

@ApiPropertyOptional({ description: 'Updated name associated with the testimonial' })
@IsString()
@IsOptional()
name?: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class UpdateTestimonialResponseDto {
status: string;
message: string;
data: any;
}

25 changes: 25 additions & 0 deletions src/modules/testimonials/testimonials.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
ParseUUIDPipe,
HttpStatus,
Delete,
Patch,
} from '@nestjs/common';
import { ApiBearerAuth, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
import { UserPayload } from '../user/interfaces/user-payload.interface';
Expand All @@ -23,6 +24,8 @@ import {
GetTestimonials400ErrorResponseDto,
GetTestimonials404ErrorResponseDto,
} from './dto/get-testimonials.dto';
import { UpdateTestimonialDto } from './dto/update-testimonial.dto';
import { UpdateTestimonialResponseDto } from './dto/update-testimonial.response.dto';

@ApiBearerAuth()
@ApiTags('Testimonials')
Expand Down Expand Up @@ -115,4 +118,26 @@ export class TestimonialsController {
async deleteTestimonial(@Param('id') id: string) {
return this.testimonialsService.deleteTestimonial(id);
}

@Patch(':id')
@ApiOperation({ summary: 'Update Testimonial' })
@ApiResponse({ status: 200, description: 'Testimonial updated successfully' })
@ApiResponse({ status: 404, description: 'Testimonial not found' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
@ApiResponse({ status: 500, description: 'Internal Server Error' })
async update(
@Param('id') id: string,
@Body() updateTestimonialDto: UpdateTestimonialDto,
@Req() req: { user: UserPayload }
): Promise<UpdateTestimonialResponseDto> {
const userId = req.user.id;

const data = await this.testimonialsService.updateTestimonial(id, updateTestimonialDto, userId);

return {
status: 'success',
message: 'Testimonial updated successfully',
data,
}
}
}
21 changes: 21 additions & 0 deletions src/modules/testimonials/testimonials.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import UserService from '../user/user.service';
import { TestimonialMapper } from './mappers/testimonial.mapper';
import { TestimonialResponseMapper } from './mappers/testimonial-response.mapper';
import { TestimonialResponse } from './interfaces/testimonial-response.interface';
import { UpdateTestimonialDto } from './dto/update-testimonial.dto';

@Injectable()
export class TestimonialsService {
Expand Down Expand Up @@ -106,6 +107,26 @@ export class TestimonialsService {

return TestimonialResponseMapper.mapToEntity(testimonial);
}

async updateTestimonial(id: string, updateTestimonialDto: UpdateTestimonialDto, userId: string) {
const testimonial = await this.testimonialRepository.findOne({ where: { id, user: { id: userId } } });

if (!testimonial) {
throw new CustomHttpException('Testimonial not found', HttpStatus.NOT_FOUND);
}

Object.assign(testimonial, updateTestimonialDto);
await this.testimonialRepository.save(testimonial);

return {
id: testimonial.id,
user_id: userId,
content: testimonial.content,
name: testimonial.name,
updated_at: new Date(),
};
}

async deleteTestimonial(id: string) {
const testimonial = await this.testimonialRepository.findOne({ where: { id } });
if (!testimonial) {
Expand Down
84 changes: 84 additions & 0 deletions src/modules/testimonials/tests/update.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { InternalServerErrorException, NotFoundException, HttpStatus } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Profile } from '../../profile/entities/profile.entity';
import { User } from '../../user/entities/user.entity';
import UserService from '../../user/user.service';
import { UpdateTestimonialDto } from '../dto/update-testimonial.dto';
import { Testimonial } from '../entities/testimonials.entity';
import { TestimonialsService } from '../testimonials.service';

describe('TestimonialsService', () => {
let service: TestimonialsService;
let userService: UserService;
let testimonialRepository: Repository<Testimonial>;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
TestimonialsService,
UserService,
{
provide: getRepositoryToken(Testimonial),
useClass: Repository,
},
{
provide: getRepositoryToken(User),
useClass: Repository,
},
{
provide: getRepositoryToken(Profile),
useClass: Repository,
},
],
}).compile();

service = module.get<TestimonialsService>(TestimonialsService);
userService = module.get<UserService>(UserService);
testimonialRepository = module.get<Repository<Testimonial>>(getRepositoryToken(Testimonial));
});

it('should be defined', () => {
expect(service).toBeDefined();
});

describe('updateTestimonial', () => {
it('should successfully update a testimonial', async () => {
const id = 'testimonial_id';
const updateTestimonialDto: UpdateTestimonialDto = {
name: 'Updated Name',
content: 'Updated content!',
};
const userId = 'user_id';
const testimonial = { id, user: { id: userId }, ...updateTestimonialDto } as Testimonial;

jest.spyOn(testimonialRepository, 'findOne').mockResolvedValue(testimonial);
jest.spyOn(testimonialRepository, 'save').mockResolvedValue(testimonial);

const result = await service.updateTestimonial(id, updateTestimonialDto, userId);

expect(result).toEqual({
id,
user_id: userId,
...updateTestimonialDto,
updated_at: expect.any(Date),
});
});

it('should throw a NotFoundException if testimonial is not found', async () => {
const id = 'testimonial_id';
const updateTestimonialDto: UpdateTestimonialDto = {
name: 'Updated Name',
content: 'Updated content!',
};
const userId = 'user_id';

jest.spyOn(testimonialRepository, 'findOne').mockResolvedValue(null);

await expect(service.updateTestimonial(id, updateTestimonialDto, userId)).rejects.toThrow(
new NotFoundException('Testimonial not found')
);
});
});
});

0 comments on commit c05ee85

Please sign in to comment.