From 3b111dd03ce2850bddba2433165d3a541bf1413c Mon Sep 17 00:00:00 2001 From: Nockk Date: Wed, 7 Aug 2024 17:31:37 +0100 Subject: [PATCH 1/6] fix: product to use superadmin as well --- src/controllers/ProductController.ts | 10 ++++++++-- src/middleware/checkUserRole.ts | 8 ++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/controllers/ProductController.ts b/src/controllers/ProductController.ts index ef6e9f4a..f095ec38 100644 --- a/src/controllers/ProductController.ts +++ b/src/controllers/ProductController.ts @@ -243,13 +243,19 @@ class ProductController { }; /** - * @openapi - * /api/v1/products/{product_id}: + * @swagger + * /api/v1/organizations/{org_id}/products/{product_id}: * delete: * summary: Delete a product by its ID * tags: [Product] * parameters: * - in: path + * name: org_id + * required: true + * schema: + * type: string + * description: The ID of the organization + * - in: path * name: product_id * required: true * schema: diff --git a/src/middleware/checkUserRole.ts b/src/middleware/checkUserRole.ts index 70b49d7c..a52c619e 100644 --- a/src/middleware/checkUserRole.ts +++ b/src/middleware/checkUserRole.ts @@ -42,8 +42,12 @@ export const checkPermissions = (roles: UserRole[]) => { export function adminOnly(req: Request, res: Response, next: NextFunction) { const user = req.user; - if (!user || user.role !== UserRole.ADMIN) { - return next(new Unauthorized("Access denied. Admins only.")); + if (!["super_admin", "admin"].includes(user.role.toLowerCase())) { + return res.status(403).json({ + status_code: 403, + message: "Access denied: user not an admin", + }); } + next(); } From 80a86f114db5720dff90877b5b9e3831433a8b56 Mon Sep 17 00:00:00 2001 From: Nockk Date: Thu, 8 Aug 2024 10:42:19 +0100 Subject: [PATCH 2/6] feat: update product add update product tests --- src/test/product.spec.ts | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/test/product.spec.ts b/src/test/product.spec.ts index e369ae37..410b16a7 100644 --- a/src/test/product.spec.ts +++ b/src/test/product.spec.ts @@ -197,4 +197,55 @@ describe("ProductService", () => { expect(productRepository.remove).not.toHaveBeenCalled(); }); }); + + describe("updateProduct", () => { + it("should successfully update a product", async () => { + const org_id = "org123"; + const product_id = "prod123"; + const mockProduct = { id: product_id, name: "Test Product" } as Product; + const updateDetails = { price: 10 }; + const updatedProduct = { ...mockProduct, ...updateDetails }; + + // Mocking checkEntities to return the product + productService["checkEntities"] = jest.fn().mockResolvedValue({ + product: mockProduct, + }); + + // Mocking the save method to return the updated product + productRepository.save = jest.fn().mockResolvedValue(updatedProduct); + + const result = await productService.updateProduct( + org_id, + product_id, + updateDetails, + ); + + expect(productRepository.save).toHaveBeenCalledWith(updatedProduct); + expect(result).toEqual(updatedProduct); + }); + + it("should throw ServerError if product update fails", async () => { + const org_id = "org123"; + const product_id = "prod123"; + const mockProduct = { id: product_id, name: "Test Product" } as Product; + const updateDetails = { price: 10 }; + + // Mocking checkEntities to return the product + productService["checkEntities"] = jest.fn().mockResolvedValue({ + product: mockProduct, + }); + + // Mocking the save method to return undefined (simulating a failure) + productRepository.save = jest.fn().mockResolvedValue(undefined); + + await expect( + productService.updateProduct(org_id, product_id, updateDetails), + ).rejects.toThrow(ServerError); + + expect(productRepository.save).toHaveBeenCalledWith({ + ...mockProduct, + ...updateDetails, + }); + }); + }); }); From 505bbd35623aafe93ccdf477890fd87c433480d3 Mon Sep 17 00:00:00 2001 From: Nockk Date: Thu, 8 Aug 2024 10:57:10 +0100 Subject: [PATCH 3/6] feat: update product --- src/middleware/checkUserRole.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/middleware/checkUserRole.ts b/src/middleware/checkUserRole.ts index a52c619e..692bac71 100644 --- a/src/middleware/checkUserRole.ts +++ b/src/middleware/checkUserRole.ts @@ -42,11 +42,8 @@ export const checkPermissions = (roles: UserRole[]) => { export function adminOnly(req: Request, res: Response, next: NextFunction) { const user = req.user; - if (!["super_admin", "admin"].includes(user.role.toLowerCase())) { - return res.status(403).json({ - status_code: 403, - message: "Access denied: user not an admin", - }); + if (!user || user.role !== UserRole.ADMIN) { + return next(new Unauthorized("Access denied. Admins only.")); } next(); From 859212a9db0ee81dc242b4e4a5f37527621da5e4 Mon Sep 17 00:00:00 2001 From: Nockk Date: Thu, 8 Aug 2024 10:59:45 +0100 Subject: [PATCH 4/6] feat: update product --- src/middleware/organizationValidation.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/middleware/organizationValidation.ts b/src/middleware/organizationValidation.ts index e8ec3bd6..9b461393 100644 --- a/src/middleware/organizationValidation.ts +++ b/src/middleware/organizationValidation.ts @@ -206,7 +206,6 @@ export const validateUserToOrg = async ( next(); } catch (error) { - console.log(error); res.status(500).json({ status_code: 500, message: "Internal server error", From d65649037409da8d954fb8fefb8950667758668e Mon Sep 17 00:00:00 2001 From: Nockk Date: Thu, 8 Aug 2024 11:22:22 +0100 Subject: [PATCH 5/6] feat: update product add swagger docs --- src/controllers/ProductController.ts | 165 +++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) diff --git a/src/controllers/ProductController.ts b/src/controllers/ProductController.ts index 09f17078..28d95791 100644 --- a/src/controllers/ProductController.ts +++ b/src/controllers/ProductController.ts @@ -195,6 +195,171 @@ class ProductController { res.status(200).json({ message: "Product deleted successfully" }); }; + /** + * @swagger + * /api/v1/{org_id}/products/{product_id}: + * patch: + * summary: Update a product + * description: Update details of a product within an organization. + * tags: + * - Products + * parameters: + * - in: path + * name: org_id + * required: true + * schema: + * type: string + * description: The ID of the organization + * - in: path + * name: product_id + * required: true + * schema: + * type: string + * description: The ID of the product + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: object + * properties: + * description: + * type: string + * example: "product 1 updated price" + * price: + * type: number + * example: 30 + * name: + * type: string + * category: + * type: string + * quantity: + * type: integer + * image: + * type: string + * size: + * type: string + * stock_status: + * type: string + * responses: + * 200: + * description: Product updated successfully + * content: + * application/json: + * schema: + * type: object + * properties: + * status_code: + * type: integer + * example: 200 + * message: + * type: string + * example: "product update successful" + * data: + * type: object + * properties: + * id: + * type: string + * example: "b14e4406-80f6-4029-9681-715798741c8f" + * name: + * type: string + * example: "Product 1" + * description: + * type: string + * example: "product 1 updated price" + * price: + * type: number + * example: 30 + * quantity: + * type: integer + * example: 1 + * category: + * type: string + * example: "test product" + * image: + * type: string + * example: "image url" + * updated_at: + * type: string + * format: date-time + * example: "2024-08-08T09:06:36.210Z" + * created_at: + * type: string + * format: date-time + * example: "2024-08-08T07:12:33.936Z" + * size: + * type: string + * example: "Standard" + * stock_status: + * type: string + * example: "low on stock" + * 400: + * description: Bad request + * content: + * application/json: + * schema: + * type: object + * properties: + * status_code: + * type: integer + * example: 400 + * message: + * type: string + * example: "user not a member of organization" + * 403: + * description: Forbidden + * content: + * application/json: + * schema: + * type: object + * properties: + * status_code: + * type: integer + * example: 403 + * message: + * type: string + * example: "Forbidden: User not an admin or super admin" + * 404: + * description: Resource not found + * content: + * application/json: + * schema: + * type: object + * properties: + * status_code: + * type: integer + * example: 404 + * message: + * type: string + * example: "Product with id {provided id} not found" + * 422: + * description: Unprocessable Entity + * content: + * application/json: + * schema: + * type: object + * properties: + * status_code: + * type: integer + * example: 422 + * message: + * type: string + * example: "Product ID not provided" + * 500: + * description: Internal server error + * content: + * application/json: + * schema: + * type: object + * properties: + * status_code: + * type: integer + * example: 500 + * message: + * type: string + * example: "Internal server error" + */ + public updateProduct = async (req: Request, res: Response) => { const { org_id, product_id } = req.params; const updatedProduct = await this.productService.updateProduct( From 4f57880e2311411db22e2da233c8305141e0a1a5 Mon Sep 17 00:00:00 2001 From: Nockk Date: Thu, 8 Aug 2024 12:10:46 +0100 Subject: [PATCH 6/6] feat: update product --- src/controllers/ProductController.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/controllers/ProductController.ts b/src/controllers/ProductController.ts index 28d95791..2e98e10c 100644 --- a/src/controllers/ProductController.ts +++ b/src/controllers/ProductController.ts @@ -198,7 +198,7 @@ class ProductController { /** * @swagger * /api/v1/{org_id}/products/{product_id}: - * patch: + * put: * summary: Update a product * description: Update details of a product within an organization. * tags: @@ -249,6 +249,9 @@ class ProductController { * schema: * type: object * properties: + * status: + * type: string + * example: "success" * status_code: * type: integer * example: 200 @@ -331,7 +334,7 @@ class ProductController { * example: 404 * message: * type: string - * example: "Product with id {provided id} not found" + * example: "Product with id b14e4406-80f6-4029-9681-715798741c8f not found" * 422: * description: Unprocessable Entity * content: