-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
165 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import request from 'supertest'; | ||
import app from '../app'; | ||
import { beforeAllHook, afterAllHook, getBuyerToken } from './testSetup'; | ||
|
||
beforeAll(beforeAllHook); | ||
afterAll(afterAllHook); | ||
|
||
describe('Search Products Controller Test', () => { | ||
let buyerToken: string; | ||
|
||
beforeAll(async () => { | ||
buyerToken = await getBuyerToken(); | ||
}); | ||
|
||
it('should search products with keyword', async () => { | ||
|
||
const response = await request(app) | ||
.get('/api/v1/search?keyword=keyword') | ||
.set('Authorization', `Bearer ${buyerToken}`); | ||
expect(response.status).toBe(200); | ||
expect(response.body.data).toBeDefined(); | ||
}); | ||
|
||
it('should search products by category', async () => { | ||
const response = await request(app) | ||
.get('/api/v1/search?category=categoryName') | ||
.set('Authorization', `Bearer ${buyerToken}`); | ||
expect(response.status).toBe(200); | ||
expect(response.body.data).toBeDefined(); | ||
}); | ||
|
||
it('should search products by product name', async () => { | ||
const response = await request(app) | ||
.get('/api/v1/search?productName=productName') | ||
.set('Authorization', `Bearer ${buyerToken}`); | ||
expect(response.status).toBe(200); | ||
expect(response.body.data).toBeDefined(); | ||
}); | ||
|
||
it('should search products and apply sorting', async () => { | ||
const response = await request(app) | ||
.get('/api/v1/search?sort=asc') | ||
.set('Authorization', `Bearer ${buyerToken}`); | ||
expect(response.status).toBe(200); | ||
expect(response.body.data).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Request, Response } from 'express'; | ||
import dbConnection from '../database'; | ||
import Product from '../database/models/productEntity'; | ||
import Category from '../database/models/categoryEntity'; | ||
import errorHandler from '../middlewares/errorHandler'; | ||
|
||
const productRepository = dbConnection.getRepository(Product); | ||
const categoryRepository = dbConnection.getRepository(Category); | ||
|
||
export const searchProducts = errorHandler( | ||
async (req: Request, res: Response) => { | ||
const { keyword, category, productName, sort } = req.query; | ||
|
||
let queryBuilder = productRepository.createQueryBuilder('product'); | ||
|
||
if (keyword) { | ||
queryBuilder = queryBuilder.andWhere( | ||
'product.name ILIKE :keyword OR product.shortDesc ILIKE :keyword OR product.longDesc ILIKE :keyword', | ||
{ keyword: `%${keyword}%` } | ||
); | ||
} | ||
|
||
if (category) { | ||
const categoryEntity = await categoryRepository.findOne({ | ||
where: { name: category as string }, | ||
}); | ||
if (categoryEntity) { | ||
queryBuilder = queryBuilder.andWhere( | ||
'product.categoryId = :categoryId', | ||
{ categoryId: categoryEntity.id } | ||
); | ||
} | ||
} | ||
|
||
if (productName) { | ||
queryBuilder = queryBuilder.andWhere('product.name ILIKE :productName', { | ||
productName: `%${productName}%`, | ||
}); | ||
} | ||
|
||
if (sort) { | ||
const sortDirection = sort.toString().toUpperCase() as 'ASC' | 'DESC'; | ||
queryBuilder = queryBuilder.orderBy('product.salesPrice', sortDirection); | ||
} | ||
|
||
const total = await queryBuilder.getCount(); | ||
const products = await queryBuilder.getMany(); | ||
|
||
return res.status(200).json({ | ||
data: products, | ||
total, | ||
}); | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/** | ||
* @swagger | ||
* /api/v1/search: | ||
* get: | ||
* summary: Search products | ||
* tags: [Buyer] | ||
* security: | ||
* - bearerAuth: [] | ||
* parameters: | ||
* - in: query | ||
* name: keyword | ||
* type: string | ||
* description: Keyword to search for in product name, short description, or long description. | ||
* - in: query | ||
* name: category | ||
* type: string | ||
* description: Name of the category to filter the products. | ||
* - in: query | ||
* name: productName | ||
* type: string | ||
* description: Name of the product to filter the products. | ||
* - in: query | ||
* name: sort | ||
* type: string | ||
* enum: [asc, desc] | ||
* description: Sort order for the products based on sales price. | ||
* responses: | ||
* '200': | ||
* description: Successful operation. Returns the list of products matching the search criteria. | ||
* '400': | ||
* description: Invalid search parameters provided. | ||
* '500': | ||
* description: Internal Server Error. | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { query, validationResult } from 'express-validator'; | ||
import { Request, Response, NextFunction } from 'express'; | ||
export const validateSearchParams = [ | ||
query('keyword').optional().isString().withMessage('Keyword must be a string'), | ||
query('category').optional().isString().withMessage('Category must be a string'), | ||
query('brand').optional().isString().withMessage('Brand must be a string'), | ||
query('productName').optional().isString().withMessage('Product name must be a string'), | ||
query('sort').optional().isIn(['asc', 'desc']).withMessage('Sort order must be either asc or desc'), | ||
(req: Request, res: Response, next: NextFunction) => { | ||
const errors = validationResult(req); | ||
if (!errors.isEmpty()) { | ||
return res.status(400).json({ errors: errors.array() }); | ||
} | ||
next(); | ||
} | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Router } from 'express'; | ||
import { searchProducts } from '../controller/searchProducts' | ||
import { validateSearchParams } from '../middlewares/validateSearchParams'; | ||
import { IsLoggedIn } from '../middlewares/isLoggedIn'; | ||
import { checkRole } from '../middlewares/authorize'; | ||
const searchRouter = Router(); | ||
|
||
searchRouter.get('/search', IsLoggedIn,checkRole(['Buyer']), validateSearchParams, searchProducts); | ||
|
||
export default searchRouter; |