From ba01dcb57bb2e5af2b6abc8b693171631a407bf4 Mon Sep 17 00:00:00 2001 From: Joslyn Manzi Karenzi Date: Thu, 30 May 2024 16:16:48 +0200 Subject: [PATCH] fix(review-swagger-docs): fix swagger documentation of thereview task (#114) - add missing security tag in review docs [Fixes #113] update profile (#72) (#104) review controller adding testing fix lint issue update profile (#72) (#104) review controller adding testing fix lint issue --- src/__test__/orderController.test.ts | 76 +++++++++++++++++++++++ src/controller/OrderTrackingController.ts | 52 ++++++++++++++++ src/database/models/orderEntity.ts | 8 ++- src/docs/cartDocs.ts | 59 ++++++++++++++++++ src/docs/reviewDocs.ts | 2 + src/routes/index.ts | 4 +- src/routes/orderRoutes.ts | 9 +++ 7 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 src/__test__/orderController.test.ts create mode 100644 src/controller/OrderTrackingController.ts create mode 100644 src/routes/orderRoutes.ts diff --git a/src/__test__/orderController.test.ts b/src/__test__/orderController.test.ts new file mode 100644 index 00000000..003470e4 --- /dev/null +++ b/src/__test__/orderController.test.ts @@ -0,0 +1,76 @@ +import request from 'supertest'; +import app from '../app'; +import { Order } from '../database/models/orderEntity'; +import { afterAllHook, beforeAllHook } from './testSetup'; +import dbConnection from '../database'; + +const orderRepository = dbConnection.getRepository(Order); + +beforeAll(beforeAllHook); +afterAll(afterAllHook); + +describe('Order Routes', () => { + describe('PUT /order/:orderId', () => { + it('should update order status to Failed', async () => { + const order = orderRepository.create({ + status: 'Pending', + totalAmount: 40, + trackingNumber: '34343653', + }); + await orderRepository.save(order); + + const response = await request(app) + .put(`/api/v1/order/${order.id}`) + .send({ status: 'Failed' }); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ msg: 'Order status updated to Failed' }); + }); + + it('should return 400 if orderId is invalid', async () => { + const response = await request(app) + .put('/api/v1/order/invalid') + .send({ status: 'Failed' }); + + expect(response.status).toBe(400); + expect(response.body).toEqual({ msg: 'Invalid orderId' }); + }); + + it('should return 400 if status is invalid', async () => { + const order = orderRepository.create({ + status: 'Pending', + totalAmount: 40, + trackingNumber: '34343653', + }); + await orderRepository.save(order); + + const response = await request(app) + .put(`/api/v1/order/${order.id}`) + .send({ status: 'InvalidStatus' }); + + expect(response.status).toBe(400); + expect(response.body).toEqual({ msg: 'Invalid status' }); + }); + + it('should return 404 if order is not found', async () => { + const response = await request(app) + .put('/api/v1/order/9999') + .send({ status: 'Failed' }); + + expect(response.status).toBe(404); + expect(response.body).toEqual({ msg: 'Order Not Found' }); + }); + + it('should return 500 if there is a server error', async () => { + jest + .spyOn(orderRepository, 'findOne') + .mockRejectedValue(new Error('Database error')); + + const response = await request(app) + .put('/api/v1/order/1') + .send({ status: 'Failed' }); + + expect(response.status).toBe(500); + }); + }); +}); diff --git a/src/controller/OrderTrackingController.ts b/src/controller/OrderTrackingController.ts new file mode 100644 index 00000000..474d8df1 --- /dev/null +++ b/src/controller/OrderTrackingController.ts @@ -0,0 +1,52 @@ +import { Request, Response } from 'express'; +import dbConnection from '../database'; +import errorHandler from '../middlewares/errorHandler'; +import { Order } from '../database/models/orderEntity'; + +const orderRepository = dbConnection.getRepository(Order); + +export const updateOrderStatus = errorHandler( + async (req: Request, res: Response) => { + const { orderId } = req.params; + const { status } = req.body; + + // Convert orderId to a number + const numericOrderId = parseInt(orderId, 10); + if (isNaN(numericOrderId)) { + return res.status(400).json({ msg: 'Invalid orderId' }); + } + + // Validating the status + const validStatuses = [ + 'Pending', + 'Failed', + 'Canceled', + 'Paid', + 'Shipping', + 'Delivered', + 'Returned', + 'Completed', + ]; + if (!validStatuses.includes(status)) { + return res.status(400).json({ msg: 'Invalid status' }); + } + + const order = await orderRepository.findOne({ + where: { + id: numericOrderId, + }, + }); + + if (!order) { + return res.status(404).json({ msg: 'Order Not Found' }); + } + + // Update the order status + order.status = status; + await orderRepository.save(order); + + return res + .status(200) + .json({ msg: `Order status updated to ${order.status}` }); + } +); diff --git a/src/database/models/orderEntity.ts b/src/database/models/orderEntity.ts index bd2962a7..8b0a0b59 100644 --- a/src/database/models/orderEntity.ts +++ b/src/database/models/orderEntity.ts @@ -39,10 +39,12 @@ export class Order { @UpdateDateColumn() updatedAt: Date; - @OneToMany(() => OrderDetails, orderDetails => orderDetails.order, { cascade: true }) + @OneToMany(() => OrderDetails, (orderDetails) => orderDetails.order, { + cascade: true, + nullable: true, + }) orderDetails: OrderDetails[]; - + @Column({ type: 'boolean', default: false, nullable: true }) paid: boolean | null; - } diff --git a/src/docs/cartDocs.ts b/src/docs/cartDocs.ts index 131f0bfa..768603ee 100644 --- a/src/docs/cartDocs.ts +++ b/src/docs/cartDocs.ts @@ -224,3 +224,62 @@ * 500: * description: Internal Server Error */ + +/** + * @swagger + * /api/v1/order/{orderId}: + * put: + * summary: Update the status of an order + * tags: [Order] + * parameters: + * - name: orderId + * in: path + * required: true + * description: ID of the order to update + * schema: + * type: integer + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * status: + * type: string + * enum: [Pending, Failed, Canceled, Paid, Shipping, Delivered, Returned, Completed] + * example: Failed + * responses: + * 200: + * description: Order status updated successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * msg: + * type: string + * example: Order status updated to Failed + * 400: + * description: Invalid request + * content: + * application/json: + * schema: + * type: object + * properties: + * msg: + * type: string + * example: Invalid status + * 404: + * description: Order not found + * content: + * application/json: + * schema: + * type: object + * properties: + * msg: + * type: string + * example: Order Not Found + * 500: + * description: Internal Server Error + */ diff --git a/src/docs/reviewDocs.ts b/src/docs/reviewDocs.ts index e06e2299..05ae5c45 100644 --- a/src/docs/reviewDocs.ts +++ b/src/docs/reviewDocs.ts @@ -4,6 +4,8 @@ * post: * summary: Create a new review * tags: [Reviews] + * security: + * - bearerAuth: [] * consumes: * - application/json * produces: diff --git a/src/routes/index.ts b/src/routes/index.ts index b2fb9092..97cfe421 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -8,6 +8,7 @@ import cartRoutes from '../routes/cartRoutes'; import couponRouter from './couponRoute'; import chekoutRoutes from './checkoutRoutes'; import reviewRoute from './reviewRoutes'; +import orderRoutes from './orderRoutes'; const router = Router(); @@ -19,6 +20,7 @@ router.use('/buyer', buyerRoutes); router.use('/cart', cartRoutes); router.use('/coupons', couponRouter); router.use('/checkout', chekoutRoutes); -router.use('/review',reviewRoute) +router.use('/review', reviewRoute); +router.use('/order', orderRoutes); export default router; diff --git a/src/routes/orderRoutes.ts b/src/routes/orderRoutes.ts new file mode 100644 index 00000000..25e98a96 --- /dev/null +++ b/src/routes/orderRoutes.ts @@ -0,0 +1,9 @@ +import { Router } from 'express'; + +import { updateOrderStatus } from '../controller/OrderTrackingController'; + +const orderRoutes = Router(); + +orderRoutes.route('/:orderId').put(updateOrderStatus); + +export default orderRoutes;