# Grace Shopper Code Review #1 Notes
## Tier 1: MVP Shopping Experience
### As a customer/visitor, I want to be able to:
- [ ] access a deployed version of the website so I can browse and purchase products.
- [ ] view all available products so I can pick from a variety.
- [ ] view a single product so I can see more details.
- [ ] add a product to my cart so I can collect my desired products in one place.
- [ ] edit my cart if I change my mind:
- [ ] change the quantity of a product in my cart.
- [ ] remove a product in my cart.
- [ ] *No one else should be able to edit my cart except me.*
- [ ] "checkout" the items in my cart so I can purchase my desired goods.
- [ ] *Think of a typical user experience on popular websites from a guest user and logged-in user perspective.*
- [ ] *You can just start with by simulating the experience of checking out with a simple confirmation page.*
- [ ] create an account so I can have a logged-in experience.
### As a logged-in customer, I want to be able to:
- [ ] have a persistent cart so I can revisit and pick up where I left off.
- [ ] *Logged-in-user across multiple devices: I'm logged in on my mobile device and add some items to my cart. When I open the browser on my laptop and log in, I want to see those items in my cart.*
- [ ] *No one else should be able to edit my cart except me.*
### As an administrator, I want to be able to:
- [ ] have validated data to ensure reliability.
- *i.e. each customer that creates an account should only be able to do so once with a single email address.*
- [ ] have full rights to make backend requests to add, edit, and remove products.
- *No one else should have access.*
- [ ] view user information.
- *No one else should have access.*
### As an engineer, I want to:
- [ ] have a well-seeded database so that I am able to simulate a number of different scenarios for the user stories below.
- [ ] *By doing this, you really set yourselves up to tackle many of the points throughout the tiers. In the long run, this will save you, potentially, tons of time.*
- [ ] *For example, seed hundreds of products with dummy data so that when you get to the “pagination” user story, you won’t have to worry about adding more products.*
- [ ] *Likewise, add a bunch of users with products in their carts so editing the cart can be worked on without already having the “add to cart” functionality built out.*
- [ ] user data to be secure so that no one can unrightfully manipulate information.
## Schema
### Minimum
At minimum, you will want to have at least 3 tables/models
- Order
- Product
- User
The associations among the table should be as follows:
- User has a many-to-many relationship with products through the order table

The reference between user and order is fine. Every order is done by one user; every user can make several orders. The problem lies within the reference between order and product or rather order being the through/junction/join table making the many-to-many relationship between user and product:
- Several products can be bought in one purchase; several purchases can include the same product. But our reference only allows one product to be bought in a single purchase
- Generating order history could prove problematic because we will have a problem figuring out distinction for an order. Order needs an identifier unique to each order. Also, what happens if a user makes multiple orders in one day?
Let’s take the first point on the product-order reference. Because a many-to-many relationship is represented in a relational database by having 2 one-to-many relationships, the product-order reference only allows one product to be bought in a single purchase. This can be mitigated in a few ways:
- You can use an array or text field or even a JSON object (all available in PostgreSQL) to store the names or IDs of purchased products. Problems with this approach:
- If you want to change the name of the product, you will need to update all ordered item instances in the array, text, JSON
- If you want to perform any type of analytics, you’d need to use different sub operations to access the ordered items
- It violates the normalization (no redundant data) aspect for relational/SQL databases
- Another way it can be mitigated is to add several ordered items columns in the table
- This puts a cap on the number of products bought

This method will naturally lead into a more optimal schema.
### Optimal
The optimal schema has 4 tables:
- Order
- Product
- User
- OrderProducts/LineItem
The associations are as follows:
- User has a one-to-many relationship with Order
- Order has a many-to-many relationship with Product through OrderProducts/LineItem/TransactionItem/OrderDetails
**One thing to remember: An a cart is an order that's not fulfilled. So, you can have a boolean or a status in your Order table to indicate the various states of an order**
The relationship with the junction table is as follows:
- An order has many items in order_products. An order_products item belongs to one particular order.
- A product may appear in many order_products. Each order_products item specified one product

### What Goes in the Junction/Thru Table?
- Order price
- This guarantees future order prices to be consistent. If you order something in 2015, and the price was $3.50 but the *inventory/product* price increased to $4.00 in 2019, you don't want past orders' prices to be changed in your order history
- Order quantity
- Same idea here. You want to separate out your product info from your order info.
**A good way to think about how data may get stored is you should ask yourself the question: *Is this piece of data exclusive to one or another table?* If not, then most likely it will go thru some version of a junction table. In the case of order price and order quantity: Technically, they are related to the Order table. But also, if you think about it, they are related to the Product Table as well because you're getting a list of products and their current prices and quantites and make an instance of an order to house them.**
index.js example
```javascript=
Order.belongsTo(User)
User.hasMany(Order)
Product.belongsToMany(Order, { through: Order_Products })
Order.belongsToMany(Product, { through: Order_Products })
```
Another index.js example (not encouraged by itself)
```javascript=
Order.belongsTo(User)
User.hasMany(Order)
Order.hasMany(Order_Products)
Order_Products.belongsTo(Order)
Product.hasMany(Order_Products)
Order_Products.belongsTo(Product)
```
However, you can do the [super many-to-many relationship](https://sequelize.org/master/manual/advanced-many-to-many.html#through-tables-versus-normal-tables-and-the--quot-super-many-to-many-association-quot-) to include both
```javascript=
Order.belongsTo(User)
User.hasMany(Order)
Product.belongsToMany(Order, { through: Order_Products })
Order.belongsToMany(Product, { through: Order_Products })
Order.belongsTo(User)
User.hasMany(Order)
Order.hasMany(Order_Products)
Order_Products.belongsTo(Order)
Product.hasMany(Order_Products)
Order_Products.belongsTo(Product)
```
This second example is an implied through table association. It’s just all written out manually.
Encourage “redundant/symmetric associations” because each instance will get the magic methods from Sequelize
Order_Products example
```javascript=
const Sequelize = require('sequelize')
const db = require('../db')
const Order_Products = db.define('order_products', {
unitPrice: {
type: Sequelize.INTEGER,
defaultValue: 0,
validate: {
min: 0
}
},
quantity: {
type: Sequelize.INTEGER,
defaultValue: 0,
validate: {
min: 0
}
}
})
module.exports = Order_Products
```
The big things to keep in mind here are that tables can represent more “abstract” concepts such as an order and when you are modeling a transaction, you will likely need 3 tables:
- One for the actual transaction
- One for the things bought and sold in the transaction
- One for the transaction items
This gives you the flexibility and scalability of your e-commerce site right off the bat. You can also add additional information on the junction table. This method is also extensible to other types of systems including but not limited to:
- Hotel booking system
- Book loan system
- Medical appointment system
**As mentioned earlier: Having a separate cart model/table is not necessary at first. Because what is a cart other than an unfulfilled order? Have a flag on the order model that shows fulfilled or unfulfilled. Or you can have a full enum of different statuses**
## Backend Validations
You should have the typical `allowNull`, `notEmpty`, `defaultValue` as necessary.
- For inventory tracking you should be using `max` and `min`
- For email, you should be using `isEmail`
- You should be thoughtful on what constitutes a string vs. text because some values can get very long
### Price in Pennies
When creating your models, one of the big things you should look for is if your are using floats. Sequelize offers floats, decimals, and integers but JavaScript only offers floats and integers. Floats are not precise. If you are using float (or decimal since it will be casted to float in JS) in your Sequelize models, open up Chrome Dev tools and do 0.1 + 0.2 === 0.3 or do 0.1 + 0.2
0.3 = 3/10 or roughly 1/3. See how you can get to 1/3 in binary.
## Checkout
When you click their checkout button, the order details should reflect in the database. That’s about it. Maybe a nice UX notification saying, “Your Order Has Been Submitted” or something.
## Guest Cart
- Local Storage (preferred for MVP)
- Server side (Express sessions/Session state)
## Local Storage [Client Side Only Reading]
- Stores data with no expiration date, and gets cleared only through JavaScript, or clearing the browser cache
- Has the most storage limit compared to cookies and sessions
- Only read on client side
- Limited to that browser you’re using
- However, can use chrome.storage API if you want to try to use local storage across browsers
- Go into a browser and do the following:
```javascript=
let coolStorage = window.localStorage
coolStorage.setItem('myDog', 'Spot')
const dog = coolStorage.getItem('myDog')
coolStorage.removeItem('myDog')
coolStorage.clear()
```
### Express Sessions/Session State [Note: Express also has cookie sessions]
**Note**: Using cookies (httpOnly) is actually a more secure way to store JWTs than local storage
npm i express-session
By default Express requests are sequential and no request can be linked to each other. There is no way to know if this request comes from a client that already performed a request previously.
Users cannot be identified without something called sessions. When implemented, every user of your app will be assigned a unique session, and this allows you to store the user state. You can store session data in
- local memory
- database
- cache
The session is attached to the request body so you access it by the following:
req.session
## Security
### Route Protection
You need to be able to protect routes especially the ones that have modifiers (create, update, delete). You should write your own gatekeeping middleware functions that let you check if the person trying to access the route is who they say they are. Stay DRY and create something like a gatekeeper.js file to house the function to be exported and used where ever needed. This can be checked in Postman or Insomnia
Some examples of protections:
- Customers shouldn't be able to see/edit/delete other customer's information and other customer's cart
- Customers shouldn't be able to edit/delete inventory
### Gatekeeping Middleware
Example one you might use to check if the user is accessing only their own information (like their own user info or their own car)
```javascript=
const requireToken = async (req, res, next) => {
try {
const token = req.headers.authorization
const user = await User.findByToken(token)
next()
} catch (e) {
next(e)
}
}
```
Example one you might use to check if the user is an admin
```javascript=
const isAdmin = (req, res, next) => {
if (!req.user.isAdmin) {
return res.status(403).send('You shall not pass!')
} else {
next()
}
}
```
### `req.body`
You should not send `req.body` directly into modifier functions like `create` or `update` otherwise someone might be able to force some information into the database that otherwise shouldn't be there
You can demo this on Insomnia or Postman (if you have no protections) with signup (and if you have an `isAdmin` flag in the database)
Put into Insomnia: `POST localhost:8080/auth/signup`
Then, using the body option, you can either use "JSON" or "URL form encoded". Here's a JSON example:
```
{
"username": "Sarah",
"password": "supercool",
"isAdmin": true
}
```
The solution is to destructure the `req.body` to destructure only the information you want
```javascript=
// Destructure req.body
const { username, password } = req.body
const user = await User.create({ username, password })
```
### Revealing Too Much Info
There is an example of this in boilermaker but basically, you may not want to send back ALL information from the database so you use the Sequelize `attributes` property
```javascript=
const users = await User.findAll({
// explicitly select only the id and username fields - even though
// users' passwords are encrypted, it won't help if we just
// send everything to anyone who asks!
attributes: ['id', 'username']
})
```
### Hiding Applications Secrets
You might use OAuth or maybe eventually Stripe. You should learn how to hide application secrets using `process.env`
Open up terminal, type `node` into the terminal and enter so they enter the node repl, and then `console.log(process)` and then check out the `env` property
Then you can do something like `process.env.GREETING = 'hello'` and show that it will pop up in the `process.env` variable by console logging again
Create a `secrets.js` file that will be `gitignore`'d where they can put your keys
And inside the code, it might look like
```javascript=
//app.js
if (process.env.NODE_ENV !== 'production') require('../secrets')
console.log('log environment variables ', process.env)
```
## GitHub Contributions && Team Dynamics
- Run `git shortlog -sne` to check your commits relative to your email
- The "noreply.github.com" one is when you do a merge
## Some Conventions to Think About
- RESTful routing
- Coding conventions
- Semicolon consistency
- Naming conventions
- Mishmash of ES5 & ES6 syntax