Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[finsihes 187354259] added endpoint to get all products #58

Merged
merged 1 commit into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,10 +1,12 @@
/* 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 logger from '../logs/config';
import sequelize from '../database/models';

export const createProduct = async (req: Request, res: Response) => {
try {
Expand All @@ -24,7 +26,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 @@ -39,14 +41,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 Down Expand Up @@ -154,3 +149,107 @@ export const markProductAsAvailable = 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' }],
niyontwali marked this conversation as resolved.
Show resolved Hide resolved
});

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' });
108 changes: 105 additions & 3 deletions src/docs/products.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +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:
security:
- bearerAuth: []
summary: Create a new product
security:
- bearerAuth: []
tags:
- Product
description: Create a new product in the store
Expand Down Expand Up @@ -65,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
Loading