Skip to content

Commit

Permalink
[finsihes 187354259] added endpoint to get all products
Browse files Browse the repository at this point in the history
added end point to get one product

added end point for a seller to delete a product from a his collection
  • Loading branch information
Munezero Ange committed May 10, 2024
1 parent 608d497 commit 97675c2
Show file tree
Hide file tree
Showing 13 changed files with 324 additions and 54 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"jsonwebtoken": "^9.0.2",
"mailgen": "^2.0.28",
"multer": "^1.4.5-lts.1",
"node-cron": "^3.0.3",
"nodemailer": "^6.9.13",
"passport": "^0.7.0",
"passport-google-oauth20": "^2.0.0",
Expand All @@ -60,6 +61,7 @@
"@types/jsonwebtoken": "^9.0.6",
"@types/multer": "^1.4.11",
"@types/node": "^20.12.6",
"@types/node-cron": "^3.0.11",
"@types/nodemailer": "^6.4.14",
"@types/passport": "^1.0.16",
"@types/passport-google-oauth20": "^2.0.14",
Expand Down
10 changes: 10 additions & 0 deletions src/config/cornJobs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import cron from 'node-cron';
import checkProductExpiryAndNotifySeller from '../helpers/checkProductExpiry';

const sheduledTasks = () => {
cron.schedule('0 0 * * *', async () => {
await checkProductExpiryAndNotifySeller();
});
};

export default sheduledTasks;
1 change: 0 additions & 1 deletion src/config/passport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ passport.use(
return done(new Error('Email not found in the Google profile'), undefined);
}

console.log(profile);
// Extract user's email from the Google profile
const email = profile.emails[0].value;

Expand Down
36 changes: 0 additions & 36 deletions src/config/passportConfig.ts

This file was deleted.

117 changes: 108 additions & 9 deletions src/controllers/productsController.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Request, Response } from 'express';
import { Transaction } from 'sequelize';
import uploadImage from '../helpers/claudinary';
import { sendInternalErrorResponse } from '../validations';
import { Product, ProductAttributes } from '../database/models/Product';
import { Size, SizeAttributes } from '../database/models/Size';
import sequelize from '../database/models';

export const createProduct = async (req: Request, res: Response) => {
try {
Expand All @@ -23,7 +25,7 @@ export const createProduct = async (req: Request, res: Response) => {
});
}
// handle images
const productImages = ['asdf', 'asdf', 'asdf', 'asdf'];
const productImages: string[] = [];
const images: unknown = req.files;
if (images instanceof Array && images.length > 3) {
for (const image of images) {
Expand All @@ -38,14 +40,7 @@ export const createProduct = async (req: Request, res: Response) => {
}

// create product
await Product.create({
sellerId,
name,
description,
categoryId,
colors,
images: productImages,
});
await Product.create({ sellerId, name, description, categoryId, colors, images: productImages });

res.status(201).json({
ok: true,
Expand All @@ -69,3 +64,107 @@ export const createSize = async (req: Request, res: Response) => {
sendInternalErrorResponse(res, error);
}
};

// Function to get all products available in the database
export const getAllProduct = async (req: Request, res: Response) => {
try {
// Fetch all products with their associated sizes
const products: any = await Product.findAll({
include: [{ model: Size, as: 'Sizes' }],
});

const currentDate = new Date();

// Filter products and remove expired sizes while keeping non-expired ones
const availableProducts = products.map((product: { Sizes: any[]; toJSON: () => any }) => {
const validSizes = product.Sizes.filter(
(size: { expiryDate: string | number | Date }) => new Date(size.expiryDate) > currentDate
);
return {
...product.toJSON(),
Sizes: validSizes,
};
});

res.status(200).json({
ok: true,
message: 'All products retrieved successfully',
data: availableProducts,
});
} catch (error) {
sendInternalErrorResponse(res, error);
}
};

// a function to get a certain product by ID
export const getProductById = async (req: Request, res: Response) => {
try {
const { productId } = req.params;
const product = await Product.findByPk(productId, {
include: [{ model: Size, as: 'Sizes' }],
});

if (!product) {
return res.status(404).json({
ok: false,
message: 'Product not found',
});
}

res.status(200).json({
ok: true,
message: 'Product retrieved successfully',
data: product,
});
} catch (error) {
sendInternalErrorResponse(res, error);
}
};

// a function to delete product by ID
export const deleteProductById = async (req: Request, res: Response) => {
let transaction: Transaction | null = null;

try {
const { id } = req.params;

transaction = await sequelize.transaction();

// Find the product by ID
const product = await Product.findByPk(id, { transaction });

if (!product) {
return res.status(404).json({
ok: false,
message: 'Product not found',
});
}

// Verify that the requesting user is the owner of the product
const userId = (await (req.user as any)).id;
if (product.sellerId !== userId) {
return res.status(403).json({
ok: false,
message: 'Unauthorized: You are not authorized to delete this product',
});
}

// deleting product related sizes
const sizes = await Size.findAll({ where: { id }, transaction });
await Size.destroy({ where: { id }, transaction });

// deleting the product itself
await product.destroy({ transaction });

await transaction.commit();

res.status(200).json({
ok: true,
message: 'Product deleted successfully',
});
} catch (error) {
// Rollback the transaction if an error occurs
if (transaction) await transaction.rollback();
sendInternalErrorResponse(res, error);
}
};
1 change: 0 additions & 1 deletion src/database/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ module.exports = {
username: process.env.DEV_DB_USER,
password: process.env.DEV_DB_PASSWORD,
database: process.env.DEV_DB_NAME,

dialect: process.env.DEV_DB_DRIVER,
port: process.env.DEV_DB_PORT,
host: process.env.DEV_DB_HOST,
Expand Down
2 changes: 2 additions & 0 deletions src/database/models/Product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import sequelize from './index';
import { UUIDV4 } from 'sequelize';
import { Category } from './Category';
import { Size } from './Size';
import User from './user';

export interface ProductAttributes {
id?: string;
Expand Down Expand Up @@ -74,5 +75,6 @@ Product.init(
);

Product.belongsTo(Category, { foreignKey: 'categoryId' });
Product.belongsTo(User, { foreignKey: 'sellerId', as: 'Users' });

Product.hasMany(Size, { foreignKey: 'productId' });
106 changes: 105 additions & 1 deletion src/docs/products.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,113 @@ tags:
description: Operations related to products

paths:
# DOCS FOR GETTING ALL THE PRODUCTS
/api/products:
get:
summary: Get all product from store excluding expired products
tags:
- Product
responses:
200:
description: Products retrieved successfully
content:
application/json:
schema:
type: object
properties:
status:
type: integer
example: 200
ok:
type: boolean
example: true
data:
$ref: "#/components/schemas/Product"
message:
type: string
example: "Available products retrieved successfully"
500:
description: "Internal Server Error"

# DOCS FOR GETTING A SPECIFIC PRODUCT
/api/products/{productId}:
get:
summary: get a specific product by ID
tags:
- Product
parameters:
- in: path
name: productId
required: true
type: string
responses:
200:
description: Products retrieved successfully
content:
application/json:
schema:
type: object
properties:
status:
type: integer
example: 200
ok:
type: boolean
example: true
data:
$ref: "#/components/schemas/Product"
message:
type: string
example: "product retrieved successfully"
404:
description: product not found
500:
description: "Internal Server Error"

# DOCS FOR DELETING A PRODUCT BY ID
/api/products/{id}:
delete:
summary: delete a specified product
security:
- bearerAuth: []
tags:
- Product
description: Delete a product from the store
parameters:
- in: path
name: id
required: true
type: string
responses:
200:
description: Products deleted successfully
content:
application/json:
schema:
type: object
properties:
status:
type: integer
example: 200
ok:
type: boolean
example: true
data:
$ref: "#/components/schemas/Product"
message:
type: string
example: "product deleted successfully"
403:
description: unauthorized
404:
description: product not found
500:
description: "Internal Server Error"
/api/{categoryId}/products:
post:
summary: Create a new product
security:
- bearerAuth: []
tags:
- Product
description: Create a new product in the store
Expand Down Expand Up @@ -63,7 +167,7 @@ paths:
type: boolean
example: true
data:
$ref: '#/components/schemas/Product'
$ref: "#/components/schemas/Product"
message:
type: string
example: Thank you for adding a new product in the store!
Expand Down
Loading

0 comments on commit 97675c2

Please sign in to comment.