Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
5e26ebe
finish up backend setup
Mannushka Mar 16, 2024
759432d
Merge pull request #1 from Mannushka/backend-setup
ianthehamster Mar 16, 2024
7b35d57
Created sequelize files for orders and products
ianthehamster Mar 16, 2024
14bf474
Seeded orders
ianthehamster Mar 16, 2024
48cf220
create and seed user table
Mannushka Mar 17, 2024
66d2011
create addresses table
Mannushka Mar 17, 2024
1d3f637
revert commit
Mannushka Mar 17, 2024
10b34fa
create addresses table
Mannushka Mar 17, 2024
a068eae
Merge pull request #2 from Mannushka/users-and-addresses-tables
ianthehamster Mar 19, 2024
0ae3389
Merged pull request from Marina
ianthehamster Mar 19, 2024
f8090d7
Updated routes
ianthehamster Mar 19, 2024
e2aca4c
Finished routes for adding orders and updating product stock. Will do…
ianthehamster Mar 20, 2024
d6b81ef
Altered user table for email
ianthehamster Mar 21, 2024
52aadcc
Added img column to the products table
ianthehamster Mar 21, 2024
32f6532
Added categories table and routers
ianthehamster Mar 22, 2024
1e27414
create users countroller and router
Mannushka Mar 22, 2024
ad67fec
create update request for user
Mannushka Mar 22, 2024
77afefd
add a new migration for addresses table
Mannushka Mar 22, 2024
84f4f6f
Merge pull request #5 from Mannushka/users
ianthehamster Mar 23, 2024
706452b
Removed shipping_details column in products table
ianthehamster Mar 24, 2024
1a29f97
Removed model_url
ianthehamster Mar 24, 2024
9feda20
Updating seeder files
ianthehamster Mar 26, 2024
3df5eb4
Updating products table with stripe id
ianthehamster Mar 26, 2024
70032d2
Merged final changes
ianthehamster Mar 26, 2024
c849447
Added quantity to order_products junction table
ianthehamster Mar 26, 2024
829db05
Committed changes to product routes
ianthehamster Mar 29, 2024
82cfbeb
Updated addresses table
ianthehamster Mar 29, 2024
72aab8b
Created seed files
ianthehamster Apr 2, 2024
6e8fb55
Deleted unused methods
ianthehamster Apr 2, 2024
b0d7a8c
Deleted unused methods
ianthehamster Apr 2, 2024
da1c7aa
Added Mailjet functionality
ianthehamster Apr 3, 2024
5148272
Deleted old junction table and created new junction table. RUN MIGRAT…
ianthehamster Apr 5, 2024
62c8bd2
Fixed error in order_products model
ianthehamster Apr 5, 2024
8d49fcf
sorting products by categories on BE
Mannushka Apr 6, 2024
0e106f7
Merge pull request #6 from Mannushka/main
Mannushka Apr 6, 2024
64ae8da
Added some draft to orders controller
ianthehamster Apr 6, 2024
0365378
Resolved merge conflicts
ianthehamster Apr 6, 2024
ff1eca5
implement pruducts route protection
Mannushka Apr 7, 2024
003e267
Merge pull request #7 from Mannushka/main
Mannushka Apr 7, 2024
36e78b5
Updated the orders controller
ianthehamster Apr 8, 2024
12c4cb0
Updated the index.js
ianthehamster Apr 8, 2024
0dcaed9
Finished the addresses and orders route for posting a new order
ianthehamster Apr 9, 2024
e99ec4f
Added remaining changes
ianthehamster Apr 10, 2024
e08d950
Merge branch 'main' of https://github.com/ianthehamster/project3-back…
ianthehamster Apr 10, 2024
ace4d57
add logic to fetch all orders of the current user
Mannushka Apr 10, 2024
783eda0
edit fetching user's orders logic
Mannushka Apr 10, 2024
5af380d
add fetching products and address along with the order
Mannushka Apr 10, 2024
bfb2464
Merge pull request #8 from Mannushka/orders
Mannushka Apr 10, 2024
28863a0
add address in getOne req
Mannushka Apr 11, 2024
141e1a2
Merge pull request #9 from Mannushka/orders
Mannushka Apr 11, 2024
8159684
fix posting a new order (item qty)
Mannushka Apr 12, 2024
b3b6060
add updating product stock when the order is posted
Mannushka Apr 12, 2024
99c6649
Merge pull request #10 from Mannushka/fix-product-qty-in-orders
Mannushka Apr 12, 2024
a5850a7
implement transaction in post order request
Mannushka Apr 13, 2024
0dd8a16
Merge pull request #11 from Mannushka/seq-transaction
Mannushka Apr 13, 2024
b3c3a31
Added flagship products
ianthehamster Apr 15, 2024
4b118e1
Added logic for posting order
ianthehamster Apr 15, 2024
49562c8
Added Stripe Ids to Flagship products
ianthehamster Apr 16, 2024
0cad869
remove logs
Mannushka Apr 17, 2024
ceb69b1
Merge pull request #12 from Mannushka/remove-logs
Mannushka Apr 17, 2024
18e5d17
add deleting a user logic
Mannushka Apr 19, 2024
77b6eba
Merge pull request #13 from Mannushka/delete-user-logic
Mannushka Apr 19, 2024
0dd4f8c
move routers out of db
Mannushka Apr 20, 2024
4c5becc
remove comments
Mannushka Apr 20, 2024
7f39c99
remove some comments
Mannushka Apr 20, 2024
9197d3d
remove unwanted comments
Mannushka Apr 20, 2024
e8b0437
Merge pull request #14 from Mannushka/refactoring
Mannushka Apr 20, 2024
31a801f
Cleaned up controllers and deleted unnecessary methods
ianthehamster Apr 20, 2024
948dc9b
Cleaned up controllers and deleted unnecessary methods
ianthehamster Apr 20, 2024
5d2efe7
delete unwanted files
Mannushka Apr 21, 2024
bdf03ef
Merge pull request #15 from Mannushka/main
Mannushka Apr 21, 2024
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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules/
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no readme

node_modules/

.env
8 changes: 8 additions & 0 deletions .sequelizerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const path = require('path');

module.exports = {
config: path.resolve('config', 'database.js'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
'migrations-path': path.resolve('db', 'migrations'),
};
11 changes: 11 additions & 0 deletions config/database.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require('dotenv').config();

module.exports = {
development: {
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
host: process.env.DB_HOST,
dialect: process.env.DB_DIALECT,
},
};
119 changes: 119 additions & 0 deletions controllers/addressesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
const BaseController = require('./baseController');
const { QueryTypes } = require('sequelize'); // Import DataTypes and QueryTypes

class AddressesController extends BaseController {
constructor(model, userModel) {
super(model);
this.userModel = userModel;
}

async getAllAddresses(req, res) {
try {
const address = await this.model.sequelize.query(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you use a raw query here?

`SELECT "id", "buyer_id", "address", "created_at" AS "createdAt", "updated_at" AS "updatedAt", "buyer_id" AS "user_id" FROM "addresses"`,
{
type: this.model.sequelize.QueryTypes.SELECT,
},
);

return res.json(address); // Assuming you expect only one address
} catch (err) {
console.error(err);
return res.status(400).send(err);
}
}

// Get a single address
async getAddress(req, res) {
const { addressId } = req.params;
try {
const address = await this.model.sequelize.query(
`SELECT "id", "buyer_id", "address", "created_at" AS "createdAt", "updated_at" AS "updatedAt", "buyer_id" AS "user_id" FROM "addresses" WHERE "id" = :addressId`,
{
replacements: { addressId },
type: this.model.sequelize.QueryTypes.SELECT,
},
);
return res.json(address[0]); // Assuming you expect only one address
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use findOne on the model then?

} catch (err) {
console.error(err);
return res.status(400).send(err);
}
}

async getAddressIdBasedOnActualAddress(req, res) {
const { delivery_address } = req.query;

try {
const address = await this.model.findOne({
where: {
address: delivery_address,
},
});

return res.status(200).json(address.id);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if no address is found? You would probably try to access id on undefined, which causes a server error. You might catch the error, but then respond with 400 (bad request), which is not the appropriate response for the issue at hand.

} catch (err) {
console.error(err);
return res.status(400).json({ error: true, msg: err });
}
}

async postNewAddress(req, res) {
const { email, address } = req.body;

const allAddresses = await this.model.sequelize.query(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use findOne above, but then go back to raw queries? Why use sequelize, if we execute raw queries only? :)

`SELECT "id", "buyer_id", "address", "created_at" AS "createdAt", "updated_at" AS "updatedAt", "buyer_id" AS "user_id" FROM "addresses"`,
{
type: this.model.sequelize.QueryTypes.SELECT,
},
);

const arrayOfAddresses = allAddresses.map(
(singleAddress) => singleAddress.address,
);

if (!arrayOfAddresses.includes(address)) {
try {
const [buyer] = await this.userModel.findOrCreate({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't create a user on the addresses controller. You should reconsider the scope of your function

where: { email: email },
});

const queryResult = await this.model.sequelize.query(
'INSERT INTO "addresses" ("buyer_id", "address", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id", "buyer_id", "address", "created_at", "updated_at"',
{
bind: [buyer.id, address, new Date(), new Date()],
type: QueryTypes.INSERT,
},
);

const newAddress = queryResult[0];

return res.json(newAddress);
} catch (err) {
console.error(err);
return res.status(400).json({ error: true, msg: err.message });
}
} else {
return res.send('Address already exists!');
}
}

async deleteOne(req, res) {
const { addressId } = req.params;

try {
await this.model.destroy({
where: {
id: addressId,
},
});

res.status(200).send(`Successfully deleted address at id ${addressId}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the confirmation that it happened successfully? All I see is you trying to destroy it, but there is no confirmation anywhere.

} catch (error) {
console.error(error);
res.status(400).send({ error: true, msg: error });
}
}
}

module.exports = AddressesController;
16 changes: 16 additions & 0 deletions controllers/baseController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class BaseController {
constructor(model) {
this.model = model;
}

async getAll(req, res) {
try {
const output = await this.model.findAll();
return res.json(output);
} catch (err) {
return res.status(400).json({ error: true, msg: err });
}
}
}

module.exports = BaseController;
70 changes: 70 additions & 0 deletions controllers/categoriesController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const BaseController = require('./baseController');

class CategoriesController extends BaseController {
constructor(model, productModel) {
super(model);
this.productModel = productModel;
}

async getOne(req, res) {
const { categoryId } = req.params;

try {
const category = await this.model.findByPk(categoryId);

res.send(category);
} catch (err) {
res.status(400).json({ error: true, msg: err });
}
}

async postOne(req, res) {
const { name } = req.body;

try {
const newCategory = await this.model.create({
name: name,
});

res.send(newCategory);
} catch (err) {
res.status(400).json({ error: true, msg: err });
}
}

async updateOne(req, res) {
const { categoryId } = req.params;

const { name } = req.body;
Comment on lines +36 to +38
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally I do not see you validate any parameters. We should check if these parameters and body values even exist. And then also if they are of the correct data type.


try {
const categoryToBeUpdated = await this.model.findByPk(categoryId);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically can check here if category exists. If no, respond to the client

const updatedCategory = await categoryToBeUpdated.update({
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if you couldn't find a category? Then this would cause your server to crash as you try to call update() on undefined

name: name,
});

res.send(updatedCategory);
} catch (err) {
return res.status(400).json({ error: true, msg: err });
}
}

async deleteOne(req, res) {
const { categoryId } = req.params;

try {
await this.model.destroy({
where: {
id: categoryId,
},
});

res.send('Category has been deleted!');
} catch (err) {
res.status(400).json({ error: true, msg: err });
}
}
}

module.exports = CategoriesController;
143 changes: 143 additions & 0 deletions controllers/ordersController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
const BaseController = require('./baseController');
const Sequelize = require('sequelize');
const sequelize = new Sequelize(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you initialize a DB in your controller? This should not happen here

`postgres://${process.env.DB_USERNAME}:@${process.env.DB_HOST}:5432/${process.env.DB_NAME}`,
);

class OrdersController extends BaseController {
constructor(model, userModel, productModel, orderProductModel, addressModel) {
super(model);
this.userModel = userModel;
this.productModel = productModel;
this.orderProductModel = orderProductModel;
this.addressModel = addressModel;
}

async getAllOrdersOfCurrUser(req, res) {
const { email } = req.query;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We definitely should validate this. Also, we should use authentication properly. I could probably query your endpoint here with any email I can find, and see if that user has any orders, what he ordered, get his address and his user id. With the user id I could then freely make requests against your user routes.

try {
const user = await this.userModel.findOrCreate({
where: { email: email },
});
const user_id = user[0].dataValues.id;
const orders = await this.model.findAll({
where: { user_id: user_id },
include: [
{
model: this.productModel,
attributes: ['id', 'title', 'price', 'img'],
through: {
model: this.orderProductModel,
attributes: ['quantity'],
},
},
{ model: this.addressModel, attributes: ['address'] },
],
});
return res.json(orders);
} catch (err) {
return res.status(400).json({ error: true, msg: err.message });
}
}

async postOne(req, res) {
const { address_id, user_id, total_price, products } = req.body;

if (!products || products.length === 0) {
return res
.status(400)
.json({ error: true, msg: 'No products in the order' });
}

try {
await sequelize.transaction(async (t) => {
const newOrder = await this.model.create(
{
address_id: address_id,
user_id: user_id,
total_price: total_price,
},
{ transaction: t },
);

const arrayOfJunctionTableEntries = [];
for (let i = 0; i < products.length; i++) {
const productObj = products[i];
const [id, quantity] = Object.entries(productObj)[0];
const newEntryInOrderProducts = await this.orderProductModel.create(
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this you are creating n amount of entries in your DB in a loop. By awaiting each item, this can take a long time potentially. You should rather try to do such operations asynchronously via Promise.all or use a bulk insert even better. Operations like here will highly increase your latency on big orders

{
order_id: newOrder.dataValues.id,
product_id: id,
quantity: quantity,
},
{ transaction: t },
);
arrayOfJunctionTableEntries.push(newEntryInOrderProducts);
}

const purchasedProducts = [];

for (let i = 0; i < products.length; i++) {
const productObj = products[i];
const [id, quantity] = Object.entries(productObj)[0];

const product = await this.productModel.findByPk(id);

const stock = product.dataValues.stock_left;

const stock_left = stock - quantity;

const updatedProduct = await product.update(
{ stock_left: stock_left },
{ transaction: t },
);
Comment on lines +84 to +93
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you loop again and make 2 separate database requests. That means you will make 2*n requests here, which you both await. This is really inefficient and you should think of an alternative way to handle this. Most likely your database design could be improved in order to facilitate this.


purchasedProducts.push(updatedProduct);
}

return res.send([
newOrder,
arrayOfJunctionTableEntries,
purchasedProducts,
]);
});
} catch (err) {
console.error(err.message);
return res.status(400).json({ error: true, msg: err });
}
}

async getOne(req, res) {
const { orderId } = req.params;

const order = await this.model.findByPk(orderId, {
include: [
{
association: 'products',
},
{ model: this.addressModel, attributes: ['address'] },
],
});

return res.send(order);
}

async deleteOne(req, res) {
const { orderId } = req.params;

try {
await this.model.destroy({
where: {
id: orderId,
},
});

res.status(200).send('Success:');
} catch (error) {
console.error(error);
res.status(400).send({ error: true, msg: error });
}
}
}

module.exports = OrdersController;
Loading