Skip to content

Commit

Permalink
Merge pull request #45 from atlp-rwanda/ft-wishlist-#187419184
Browse files Browse the repository at this point in the history
#187419184 A buyer should be able to wish a product
  • Loading branch information
teerenzo authored May 7, 2024
2 parents 435a51d + 30cf5e2 commit 975954c
Show file tree
Hide file tree
Showing 15 changed files with 865 additions and 5 deletions.
55 changes: 54 additions & 1 deletion __test__/product.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ const product:any = {
categoryID: 1,
};

const dummyBuyer = {
name: "test user",
username: "testUser",
email: "[email protected]",
password: "soleil00",
}


describe("Testing product Routes", () => {
beforeAll(async () => {
Expand Down Expand Up @@ -76,7 +83,23 @@ describe("Testing product Routes", () => {
.send(userData);
expect(response.status).toBe(201);
}, 20000);


test('should return 201 and register a dummy buyer user', async () => {
const response = await request(app)
.post("/api/v1/users/register")
.send(dummyBuyer);
expect(response.status).toBe(201);
})
let buyerToken: any;

test("should login an buyer", async () =>{
const response = await request(app).post("/api/v1/users/login").send({
email: "[email protected]",
password: "soleil00"
})
buyerToken = response.body.token;
});

let token:any, adminToken:any;
test('It should return status 401 for unthorized',async() =>{
const response = await request(app)
Expand Down Expand Up @@ -227,6 +250,35 @@ test("should return all products in db --> given '/api/v1/products'", async () =
expect(response.status).toBe(201);
},40000);

test('It should add a product to the user wishes', async () => {
const response = await request(app)
.post('/api/v1/wishes')
.send({ productId })
.set("Authorization", "Bearer " + buyerToken);
expect(response.status).toBe(201)
}, 20000);

test('It should return a list of user wishes', async () => {
const response = await request(app)
.get('/api/v1/wishes')
.set("Authorization", "Bearer " + buyerToken);
expect(response.status).toBe(200)
});

test('It should retrieve wishes on a single product', async () => {
const response = await request(app)
.get(`/api/v1/wishes/${productId}`)
.set("Authorization", "Bearer " + token);
expect(response.status).toBe(200)
})

test('It should remove a product from user wishlist', async () => {
const response = await request(app)
.delete(`/api/v1/wishes/${productId}`)
.set("Authorization", "Bearer " + buyerToken);
expect(response.status).toBe(200)
})

test('It should return status 200 for removed Product',async() =>{
const response = await request(app)
.delete(`/api/v1/products/${productId}`)
Expand All @@ -247,5 +299,6 @@ test('It should return status 200 for removed category',async() =>{
.set("Authorization", "Bearer " + token);
expect(response.status).toBe(200);
},20000);

});

112 changes: 112 additions & 0 deletions src/controllers/wishesController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as wishlistService from "../services/wishlist.service";
import { Request, Response } from "express";
import { wishSchema } from "../schemas/wishShema";

export const addToWishes = async (req: Request, res: Response) => {
//@ts-ignore
const id = req.user.id;
try {
const { error, value } = wishSchema.validate(req.body);
if (error) {
return res.status(400).json({
status: "Error",
message: error.details[0].message,
});
}
const product = await wishlistService.getProduct(value.productId);
if(!product){
return res.status(404).json({
message: 'product not found'
})
}
const isProdExisting = await wishlistService.getSingleWish(id, value.productId);
if (isProdExisting) {
return res.status(409).json({
message: "product already exists in your wishlist",
});
}
const wish = await wishlistService.addToWishlist(id, value.productId);
if (wish) {
return res.status(201).json({
message: "product was added to your wishlist",
});
}
} catch (err: any) {
return res.status(500).json({
message: err.message,
});
}
};

export const getUserWishes = async (req: Request, res: Response) => {
const { user } = req;
try {
//@ts-ignore
const wishes = await wishlistService.getAllUserWishes(user.id);
if (wishes.length == 0) {
return res.status(200).json({
message: "No wishes found",
});
} else {
return res.status(200).json({
message: "wishes was retrieved successfully",
wishes,
});
}
} catch (err: any) {
return res.status(500).json({
message: err.message,
});
}
};

export const getProductWishes = async (req: Request, res: Response) => {
const productId = req.params.id;
try {
//@ts-ignore
const isOwner = await wishlistService.checkOwnership(Number(productId), req.user.id);
if (!isOwner) {
return res.status(403).json({
message: "you're not allowed to access this",
});
}
const wishes = await wishlistService.getProductWishes(Number(productId));
if (wishes.length === 0) {
return res.status(200).json({
message: "no wishes were made on this product",
});
} else {
return res.status(200).json({
message: "success",
wishes,
});
}
} catch (err: any) {
return res.status(500).json({
message: err.message,
});
}
};

export const deleteWish = async (req: Request, res: Response) => {
//@ts-ignore
const id = req.user.id;
const productId = Number(req.params.id);
try {
const isOwner = await wishlistService.getSingleWish(id, productId);
if (!isOwner) {
return res.status(404).json({
message: "wish product does not exist",
});
} else {
await wishlistService.removeProduct(id, productId);
return res.status(200).json({
message: "product was removed from your wishes",
});
}
} catch (err: any) {
return res.status(500).json({
message: err.message,
});
}
};
17 changes: 14 additions & 3 deletions src/docs/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
deleteCategories,
categorySchema
} from "./categories";
import { AddToWishes, deleteWish, getWishes, getWishesByProduct, wishSchema } from "./wishes";

const docRouter = express.Router();

Expand Down Expand Up @@ -67,7 +68,8 @@ const options = {
description: "Endpoints related to roles"
},
{ name: "Products", description: "Endpoints related to products" },
{ name: "Categories", description: "Endpoints related categories" }
{ name: "Categories", description: "Endpoints related categories" },
{ name: "Wishes", description: "Endpoints related to Wishes" }
],

paths: {
Expand Down Expand Up @@ -121,7 +123,15 @@ const options = {
},
"/api/v1/users/{userId}/status": {
patch: changeUserAccountStatus,
}
},
"/api/v1/wishes": {
post: AddToWishes,
get: getWishes
},
"/api/v1/wishes/{id}": {
get: getWishesByProduct,
delete: deleteWish
}
},

components: {
Expand All @@ -132,7 +142,8 @@ const options = {
updatePassword: updatePasswordSchema,
Profile: profileSchema,
Product:productSchema,
Category:categorySchema
Category:categorySchema,
Wish: wishSchema
},
securitySchemes: {
bearerAuth: {
Expand Down
107 changes: 107 additions & 0 deletions src/docs/wishes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
export const wishSchema = {
type: 'object',
properties: {
productId: {
type: 'number'
},
userId: {
type: 'number'
}
}
}

export const AddToWishes = {
tags: ["Wishes"],
security: [{ bearerAuth: [] }],
summary: "Add a product to the wishlist",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
productId: {
type: "number",
},
},
},
},
},
},
responses: {
201: {
description: "Created",
},
404: { description: "Not Found"
},

},
}

export const getWishes = {
tags: ["Wishes"],
security: [{ bearerAuth: [] }],
summary: "Get all wished products",
responses: {
200: {
description: "success",
},
500: {
description: "Internal Server Error"
}
},
}

export const getWishesByProduct = {
tags: ["Wishes"],
security: [{ bearerAuth: [] }],
summary: "Get all wishes on a single products",
parameters: [
{
name: "id",
in: "path",
required: true,
description: "Product Id",
schema: {
type: "number",
},
},
],
responses: {
200: {
description: "success",
},
403: {
description: "Forbidden"
},
404: {
description: "Not found"
}
},
}

export const deleteWish = {
tags: ["Wishes"],
security: [{ bearerAuth: [] }],
summary: "Remove a product from your wishlist",
parameters: [
{
name: "id",
in: "path",
required: true,
description: "Product Id",
schema: {
type: "number",
},
},
],
responses: {
200: {
description: "success",
},
404: {
description: "Not Found"
}
},
}
20 changes: 20 additions & 0 deletions src/middlewares/isAbuyer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Request,Response, NextFunction} from "express";
import { isLoggedIn } from "./isLoggedIn";
import { Role } from "../sequelize/models/roles";

export const isAbuyer = async(req:Request,res:Response,next:NextFunction) => {
try {
await isLoggedIn(req,res,() => {});
//@ts-ignore
const roleId = req.user.roleId;
const role = await Role.findByPk(roleId);
if(role?.name === 'buyer'){
next();
}else{
res.status(403).json({message:"Forbidden: only buyers can perform this"});
}
} catch (error:any) {
console.error("Error:", error);
res.status(500).json({ message: "Internal Server Error" });
}
}
4 changes: 3 additions & 1 deletion src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import { Router } from "express";
import userRoutes from "./userRoutes";
import productsRouter from "./productsRoute";
import categoriesRouter from "./categoriesRoutes";
import wishesRouter from "./wishesRoutes";
const appROutes = Router();

appROutes.use("/users", userRoutes);
appROutes.use("/products",productsRouter);
appROutes.use('/categories',categoriesRouter)
appROutes.use('/categories',categoriesRouter);
appROutes.use("/wishes", wishesRouter);
export default appROutes;
14 changes: 14 additions & 0 deletions src/routes/wishesRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import express from 'express'
import * as wishesController from '../controllers/wishesController'
import { isLoggedIn } from '../middlewares/isLoggedIn'
import { isAseller } from '../middlewares/sellerAuth'
import { isAbuyer } from '../middlewares/isAbuyer'

const wishesRouter = express.Router()

wishesRouter.post('/', isLoggedIn, isAbuyer, wishesController.addToWishes)
wishesRouter.get('/', isLoggedIn, isAbuyer, wishesController.getUserWishes)
wishesRouter.delete('/:id', isLoggedIn, isAbuyer, wishesController.deleteWish)
wishesRouter.get('/:id', isLoggedIn, isAseller, wishesController.getProductWishes)

export default wishesRouter
Loading

0 comments on commit 975954c

Please sign in to comment.