- In this project we will work feature wise. That means we pick one object like user, book, blog, etc at a time. We work through it's feature. The steps would be:
- We create it's model.
- We build it's APIs.
- We test these APIs.
- We deploy these APIs.
- We integrate these APIs with frontend.
- We will repeat steps from Step 1 to Step 5 for each feature in this project.
- This project is divided into 4 features namely User, Product, Cart and Order. You need to work on a single feature at a time. Once that is completed as per above mentioned steps. You will be instructed to move to next Feature.
- In this project we are changing how we send token with a request. Instead of using a custom header key like x-api-key, you need to use Authorization header and send the JWT token as Bearer token.
- Create a group database
groupXDatabase
. You can clean the db you previously used and resue that. - This time each group should have a single git branch. Coordinate amongst yourselves by ensuring every next person pulls the code last pushed by a team mate. You branch will be checked as part of the demo. Branch name should follow the naming convention
project/productsManagementGroupX
- Follow the naming conventions exactly as instructed.
- User Model
{
fname: {string, mandatory},
lname: {string, mandatory},
email: {string, mandatory, valid email, unique},
profileImage: {string, mandatory}, // s3 link
phone: {string, mandatory, unique, valid Indian mobile number},
password: {string, mandatory, minLen 8, maxLen 15}, // encrypted password
address: {
shipping: {
street: {string, mandatory},
city: {string, mandatory},
pincode: {number, mandatory}
},
billing: {
street: {string, mandatory},
city: {string, mandatory},
pincode: {number, mandatory}
}
},
createdAt: {timestamp},
updatedAt: {timestamp}
}
- Create a user document from request body. Request body must contain image.
- Upload image to S3 bucket and save it's public url in user document.
- Save password in encrypted format. (use bcrypt)
- Response format
{
"status": true,
"message": "User created successfully",
"data": {
"fname": "John",
"lname": "Doe",
"email": "[email protected]",
"profileImage": "https://classroom-training-bucket.s3.ap-south-1.amazonaws.com/user/copernico-p_kICQCOM4s-unsplash.jpg",
"phone": 9876543210,
"password": "$2b$10$DpOSGb0B7cT0f6L95RnpWO2P/AtEoE6OF9diIiAEP7QrTMaV29Kmm",
"address": {
"shipping": {
"street": "MG Road",
"city": "Indore",
"pincode": 452001
},
"billing": {
"street": "MG Road",
"city": "Indore",
"pincode": 452001
}
},
"_id": "6162876abdcb70afeeaf9cf5",
"createdAt": "2021-10-10T06:25:46.051Z",
"updatedAt": "2021-10-10T06:25:46.051Z",
"__v": 0
}
}
- Allow an user to login with their email and password.
- On a successful login attempt return the userId and a JWT token contatining the userId, exp, iat.
NOTE: There is a slight change in response body. You should also return userId in addition to the JWT token.
- Response format
{
"status": true,
"message": "User login successfull",
"data": {
"userId": "6165f29cfe83625cf2c10a5c",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2MTYyODc2YWJkY2I3MGFmZWVhZjljZjUiLCJpYXQiOjE2MzM4NDczNzYsImV4cCI6MTYzMzg4MzM3Nn0.PgcBPLLg4J01Hyin-zR6BCk7JHBY-RpuWMG_oIK7aV8"
}
}
- Allow an user to fetch details of their profile.
- Make sure that userId in url param and in token is same
- Response format
{
"status": true,
"message": "User profile details",
"data": {
"address": {
"shipping": {
"street": "MG Road",
"city": "Indore",
"pincode": 452001
},
"billing": {
"street": "MG Road",
"city": "Indore",
"pincode": 452001
}
},
"_id": "6162876abdcb70afeeaf9cf5",
"fname": "John",
"lname": "Doe",
"email": "[email protected]",
"profileImage": "https://classroom-training-bucket.s3.ap-south-1.amazonaws.com/user/copernico-p_kICQCOM4s-unsplash.jpg",
"phone": 9876543210,
"password": "$2b$10$DpOSGb0B7cT0f6L95RnpWO2P/AtEoE6OF9diIiAEP7QrTMaV29Kmm",
"createdAt": "2021-10-10T06:25:46.051Z",
"updatedAt": "2021-10-10T06:25:46.051Z",
"__v": 0
}
}
- Allow an user to update their profile.
- A user can update all the fields
- Make sure that userId in url param and in token is same
- Response format
{
"status": true,
"message": "User profile updated",
"data": {
"address": {
"shipping": {
"street": "MG Road",
"city": "Delhi",
"pincode": 110001
},
"billing": {
"street": "MG Road",
"city": "Indore",
"pincode": 452010
}
},
"_id": "6162876abdcb70afeeaf9cf5",
"fname": "Jane",
"lname": "Austin",
"email": "[email protected]",
"profileImage": "https://classroom-training-bucket.s3.ap-south-1.amazonaws.com/user/laura-davidson-QBAH4IldaZY-unsplash.jpg",
"phone": 9876543210,
"password": "$2b$10$jgF/j/clYBq.3uly6Tijce4GEGJn9EIXEcw9NI3prgKwJ/6.sWT6O",
"createdAt": "2021-10-10T06:25:46.051Z",
"updatedAt": "2021-10-10T08:47:15.297Z",
"__v": 0
}
}
- Product Model
{
title: {string, mandatory, unique},
description: {string, mandatory},
price: {number, mandatory, valid number/decimal},
currencyId: {string, mandatory, INR},
currencyFormat: {string, mandatory, Rupee symbol},
isFreeShipping: {boolean, default: false},
productImage: {string, mandatory}, // s3 link
style: {string},
availableSizes: {array of string, at least one size, enum["S", "XS","M","X", "L","XXL", "XL"]},
installments: {number},
deletedAt: {Date, when the document is deleted},
isDeleted: {boolean, default: false},
createdAt: {timestamp},
updatedAt: {timestamp},
}
- Create a product document from request body.
- Upload product image to S3 bucket and save image public url in document.
- Response format
- Returns all products in the collection that aren't deleted.
- Filters
- Size (The key for this filter will be 'size')
- Product name (The key for this filter will be 'name'). You should return all the products with name containing the substring recieved in this filter
- Price : greater than or less than a specific value. The keys are 'priceGreaterThan' and 'priceLessThan'.
- Filters
NOTE: For price filter request could contain both or any one of the keys. For example the query in the request could look like { priceGreaterThan: 500, priceLessThan: 2000 } or just { priceLessThan: 1000 } )
- Sort
- Sorted by product price in ascending or descending. The key value pair will look like {priceSort : 1} or {priceSort : -1} eg /products?size=XL&name=Nit%20grit
- Response format
- Returns product details by product id
- Response format
- Updates a product by changing at least one or all fields
- Check if the productId exists (must have isDeleted false and is present in collection). If it doesn't, return an HTTP status 404 with a response body like this
- Response format
- Deletes a product by product id if it's not already deleted
- Response format
- Cart Model
{
userId: {ObjectId, refs to User, mandatory, unique},
items: [{
productId: {ObjectId, refs to Product model, mandatory},
quantity: {number, mandatory, min 1}
}],
totalPrice: {number, mandatory, comment: "Holds total price of all the items in the cart"},
totalItems: {number, mandatory, comment: "Holds total number of items in the cart"},
createdAt: {timestamp},
updatedAt: {timestamp},
}
- Create a cart for the user if it does not exist. Else add product(s) in cart.
- Get cart id in request body.
- Get productId in request body.
- Make sure that cart exist.
- Add a product(s) for a user in the cart.
- Make sure the userId in params and in JWT token match.
- Make sure the user exist
- Make sure the product(s) are valid and not deleted.
- Get product(s) details in response body.
- Response format
- Updates a cart by either decrementing the quantity of a product by 1 or deleting a product from the cart.
- Get cart id in request body.
- Get productId in request body.
- Get key 'removeProduct' in request body.
- Make sure that cart exist.
- Key 'removeProduct' denotes whether a product is to be removed({removeProduct: 0}) or its quantity has to be decremented by 1({removeProduct: 1}).
- Make sure the userId in params and in JWT token match.
- Make sure the user exist
- Get product(s) details in response body.
- Check if the productId exists and is not deleted before updating the cart.
- Response format
- Returns cart summary of the user.
- Make sure that cart exist.
- Make sure the userId in params and in JWT token match.
- Make sure the user exist
- Get product(s) details in response body.
- Response format
- Deletes the cart for the user.
- Make sure that cart exist.
- Make sure the userId in params and in JWT token match.
- Make sure the user exist
- cart deleting means array of items is empty, totalItems is 0, totalPrice is 0.
- Response format
- Order Model
{
userId: {ObjectId, refs to User, mandatory},
items: [{
productId: {ObjectId, refs to Product model, mandatory},
quantity: {number, mandatory, min 1}
}],
totalPrice: {number, mandatory, comment: "Holds total price of all the items in the cart"},
totalItems: {number, mandatory, comment: "Holds total number of items in the cart"},
totalQuantity: {number, mandatory, comment: "Holds total number of quantity in the cart"},
cancellable: {boolean, default: true},
status: {string, default: 'pending', enum[pending, completed, cancled]},
deletedAt: {Date, when the document is deleted},
isDeleted: {boolean, default: false},
createdAt: {timestamp},
updatedAt: {timestamp},
}
- Create an order for the user
- Make sure the userId in params and in JWT token match.
- Make sure the user exist
- Get cart details in the request body
- Response format
- Updates an order status
- Make sure the userId in params and in JWT token match.
- Make sure the user exist
- Get order id in request body
- Make sure the order belongs to the user
- Make sure that only a cancellable order could be canceled. Else send an appropriate error message and response.
- Response format
- To test these apis create a new collection in Postman named Project 5 Shopping Cart
- Each api should have a new request in this collection
- Each request in the collection should be rightly named. Eg Create user, Create product, Get products etc
- Each member of each team should have their tests in running state
{
status: true,
message: 'Success',
data: {
}
}
{
status: false,
message: ""
}
{
_id: ObjectId("88abc190ef0288abc190ef02"),
fname: 'John',
lname: 'Doe',
email: '[email protected]',
profileImage: 'http://function-up-test.s3.amazonaws.com/users/user/johndoe.jpg', // s3 link
phone: 9876543210,
password: '$2b$10$O.hrbBPCioVm237nAHYQ5OZy6k15TOoQSFhTT.recHBfQpZhM55Ty', // encrypted password
address: {
shipping: {
street: "110, Ridhi Sidhi Tower",
city: "Jaipur",
pincode: 400001
}, {mandatory}
billing: {
street: "110, Ridhi Sidhi Tower",
city: "Jaipur",
pincode: 400001
}
},
createdAt: "2021-09-17T04:25:07.803Z",
updatedAt: "2021-09-17T04:25:07.803Z",
}
{
_id: ObjectId("88abc190ef0288abc190ef55"),
title: 'Nit Grit',
description: 'Dummy description',
price: 23.0,
currencyId: 'INR',
currencyFormat: '₹',
isFreeShipping: false,
productImage: 'http://function-up-test.s3.amazonaws.com/products/product/nitgrit.jpg', // s3 link
style: 'Colloar',
availableSizes: ["S", "XS","M","X", "L","XXL", "XL"],
installments: 5,
deletedAt: null,
isDeleted: false,
createdAt: "2021-09-17T04:25:07.803Z",
updatedAt: "2021-09-17T04:25:07.803Z",
}
{
"_id": ObjectId("88abc190ef0288abc190ef88"),
userId: ObjectId("88abc190ef0288abc190ef02"),
items: [{
productId: ObjectId("88abc190ef0288abc190ef55"),
quantity: 2
}, {
productId: ObjectId("88abc190ef0288abc190ef60"),
quantity: 1
}],
totalPrice: 50.99,
totalItems: 2,
createdAt: "2021-09-17T04:25:07.803Z",
updatedAt: "2021-09-17T04:25:07.803Z",
}
{
"_id": ObjectId("88abc190ef0288abc190ef88"),
userId: ObjectId("88abc190ef0288abc190ef02"),
items: [{
productId: ObjectId("88abc190ef0288abc190ef55"),
quantity: 2
}, {
productId: ObjectId("88abc190ef0288abc190ef60"),
quantity: 1
}],
totalPrice: 50.99,
totalItems: 2,
totalQuantity: 3,
cancellable: true,
status: 'pending'
createdAt: "2021-09-17T04:25:07.803Z",
updatedAt: "2021-09-17T04:25:07.803Z",
}