Skip to content

Commit

Permalink
update profile (#72)
Browse files Browse the repository at this point in the history
review controller

adding testing

fix lint issue
  • Loading branch information
Dawaic6 committed May 28, 2024
1 parent 4232bbb commit 8b47be9
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 3 deletions.
94 changes: 94 additions & 0 deletions src/__test__/review.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import request from 'supertest';
import app from '../app';
import {
afterAllHook,
beforeAllHook,
getBuyerToken,
getVendorToken,
} from './testSetup';
beforeAll(beforeAllHook);
afterAll(afterAllHook);

describe('Review controller test', () => {
let buyerToken: string;
let vendorToken: string;
let productId: number;
let categoryId: number;

beforeAll(async () => {
buyerToken = await getBuyerToken();
vendorToken = await getVendorToken();
});

it('should return review product has successfully', async () => {
const categoryData = {
name: 'Category4',
description: 'category description',
};

const categoryResponse = await request(app)
.post('/api/v1/category')
.set('Authorization', `Bearer ${vendorToken}`)
.send(categoryData);

categoryId = categoryResponse.body.data.id;

const productData = {
name: 'New Product Two',
image: 'new_product.jpg',
gallery: [],
shortDesc: 'This is a new product',
longDesc: 'Detailed description of the new product',
categoryId: categoryId,
quantity: 10,
regularPrice: 5,
salesPrice: 4,
tags: ['tag1', 'tag2'],
type: 'Simple',
isAvailable: true,
};

const responseProduct = await request(app)
.post('/api/v1/product')
.set('Authorization', `Bearer ${vendorToken}`)
.send(productData);


productId = responseProduct.body.data.id;
const reviewBody = {content:'good', rating:5, productId}
const responseReview = await request(app)
.post('/api/v1/review')
.set('Authorization', `Bearer ${buyerToken}`)
.send(reviewBody);
expect(responseReview.statusCode).toEqual(201);
expect(responseReview.body.message).toEqual('Review created successfully');
expect(responseReview.body.review).toBeDefined();
}),

it('should return 404 if product is not found', async () => {
const reviewBody = {content:'good', rating:5, productId:99}
const responseReview = await request(app)
.post('/api/v1/review')
.set('Authorization', `Bearer ${buyerToken}`)
.send(reviewBody);
expect(responseReview.statusCode).toEqual(404);
expect(responseReview.body.message).toEqual('Product not found');
}),
it('should return 200 Ok to get all reviews ', async () => {
const responseReview = await request(app)
.get('/api/v1/review')
.set('Authorization', `Bearer ${buyerToken}`)
expect(responseReview.statusCode).toEqual(200);
expect(responseReview.body.reviews).toBeDefined();
}),
it('should return 409 if the review exist', async () => {
const reviewBody = {content:'good', rating:5, productId}
const responseReview = await request(app)
.post('/api/v1/review')
.set('Authorization', `Bearer ${buyerToken}`)
.send(reviewBody)
expect(responseReview.statusCode).toEqual(409);
expect(responseReview.body.message).toEqual('you are already reviewed the product');

})
})
4 changes: 2 additions & 2 deletions src/config/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ const staging = {
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
name: process.env.DB_NAME,
name: process.env.DB_NAME_DEV,
};
const production = {
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
host: process.env.DB_HOST,
port: process.env.DB_PORT,
name: process.env.DB_NAME,
name: process.env.DB_NAME_DEV,
};

const config: {
Expand Down
9 changes: 8 additions & 1 deletion src/controller/productController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,15 @@ export const getProduct = errorHandler(async (req: Request, res: Response) => {
vendor: {
firstName: true,
},
reviews:{
content:true,
rating:true,
user:{
firstName:true
}
},
},
relations: ['category', 'vendor'],
relations: ['category', 'vendor','reviews'],
});

if (!product) {
Expand Down
91 changes: 91 additions & 0 deletions src/controller/reviewController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Request, Response } from 'express';
import { Review } from '../database/models/reviewEntity';
import Product from '../database/models/productEntity';
import dbConnection from '../database';
import errorHandler from '../middlewares/errorHandler';
import UserModel from '../database/models/userModel';

const productRepository = dbConnection.getRepository(Product);
const reviewRepository = dbConnection.getRepository(Review);
const userRepository = dbConnection.getRepository(UserModel);

export const createReview = errorHandler(async (req: Request, res: Response) => {
const { content, rating, productId } = req.body;
const userId = req.user!.id;

const product = await productRepository.findOne({
where:{
id:productId
}
});
if (!product) {
return res.status(404).json({ message: 'Product not found' });
}

const user = await userRepository.findOne({
where: {
id: userId,
},
select: {
id: true,
},
});

const existingReview = await reviewRepository.findOne({
where:{
user:{
id:userId
},
product:{
id:productId
}
}
})

if(existingReview){
return res.status(409).json({ message: 'you are already reviewed the product' });
}

const newReview = new Review();
newReview.content= content;
newReview.rating=parseInt(rating) ;
newReview.user = user!;
newReview.product = product;

const review = await reviewRepository.save(newReview);

const reviews = await reviewRepository.find({
where:{
product:{
id:productId
}
},
relations:['user','product']
});
let totalRating = 0;
for (const review of reviews) {
totalRating += review.rating;
}
product.averageRating = Number((totalRating / reviews.length).toPrecision(2));


await productRepository.save(product);

return res.status(201).json({ message: 'Review created successfully', review });
});


export const getReviews = errorHandler(async (req: Request, res: Response) => {
const reviews = await reviewRepository.find({
select:{
user:{
firstName:true
},
product:{
name:true
}
},
relations:['user','product']
})
return res.status(200).json({ reviews });
});
8 changes: 8 additions & 0 deletions src/database/models/productEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
OneToMany,
} from 'typeorm';
import Category from './categoryEntity';
import UserModel from './userModel';
import { Review } from './reviewEntity';

@Entity()
export default class Product {
Expand Down Expand Up @@ -49,6 +51,12 @@ export default class Product {

@Column({ default: true })
isAvailable: boolean;

@Column('float',{ default:0})
averageRating: number;

@OneToMany(() => Review, review => review.product)
reviews: Review[];

@ManyToOne(() => UserModel, { onDelete: 'CASCADE' })
vendor: UserModel;
Expand Down
22 changes: 22 additions & 0 deletions src/database/models/reviewEntity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import Product from './productEntity';
import User from './userModel';

@Entity()
export class Review {
@PrimaryGeneratedColumn()
id: number;

@Column()
content: string;

@Column()
rating: number;

@ManyToOne(() => User, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
user: User;

@ManyToOne(() => Product, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
product: Product;
}
66 changes: 66 additions & 0 deletions src/docs/reviewDocs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* @swagger
* /api/v1/review:
* post:
* summary: Create a new review
* tags: [Reviews]
* consumes:
* - application/json
* produces:
* - application/json
* parameters:
* - in: body
* name: review
* description: The review object
* required: true
* schema:
* type: object
* properties:
* content:
* type: string
* description: The content of the review
* rating:
* type: integer
* description: The rating of the review (1 to 5)
* productId:
* type: integer
* description: The ID of the product being reviewed
* example:
* content: "This product is amazing!"
* rating: 5
* productId: 50
* responses:
* '201':
* description: Review created successfully
* schema:
* type: object
* properties:
* message:
* type: string
* description: A success message
* review:
* '400':
* description: Bad request, check the request body
* '404':
* description: Product not found
* '409':
* description: User has already reviewed the product
*/

/**
* @swagger
* /api/v1/review:
* get:
* summary: Get reviews by user
* tags: [Reviews]
* security:
* - bearerAuth: []
* responses:
* '200':
* description: Reviews retrieved successfully
* schema:
* type: object
* properties:
* reviews:
* type: array
*/
3 changes: 3 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import buyerRoutes from './buyerRoutes';
import cartRoutes from '../routes/cartRoutes';
import couponRouter from './couponRoute';
import chekoutRoutes from './checkoutRoutes';
import reviewRoute from './reviewRoutes';

const router = Router();

router.use('/user', userRouter);
Expand All @@ -17,5 +19,6 @@ router.use('/buyer', buyerRoutes);
router.use('/cart', cartRoutes);
router.use('/coupons', couponRouter);
router.use('/checkout', chekoutRoutes);
router.use('/review',reviewRoute)

export default router;
11 changes: 11 additions & 0 deletions src/routes/reviewRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

import {createReview ,getReviews} from '../controller/reviewController';
import { Router } from 'express';
import { IsLoggedIn } from '../middlewares/isLoggedIn';
import { checkRole } from '../middlewares/authorize';

const reviewRoute = Router();
reviewRoute.use(IsLoggedIn , checkRole(['Buyer']))
reviewRoute.route('/').post( createReview).get(getReviews)

export default reviewRoute;

0 comments on commit 8b47be9

Please sign in to comment.