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

#187419169 As a user, I can log into the E-commerce application via email & password #11

Closed
wants to merge 10 commits into from
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
/node_modules
/.env
/package-lock.json
/package-lock.json
dist
middleware
/coverage
31 changes: 31 additions & 0 deletions __test__/user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,35 @@ describe("Testing user Routes", () => {
expect(spy).toHaveBeenCalled();
expect(spy2).toHaveBeenCalled();
}, 20000);

test("Should return status 200 to indicate that user logged in ",async() =>{
const loggedInUser ={
email:"[email protected]",
password:`1234567`,
}
const spyonOne = jest.spyOn(User,"findOne").mockResolvedValueOnce({
//@ts-ignore
email:"[email protected]",
password:"$2a$10$zonhxs7J7h3ls7nv781Xoea1X4dJoui3t0/7gRPR3Ek4Rgzg6RAQe",
})
const response = await request(app).post("/api/v1/users/login")
.send(loggedInUser)
expect(response.status).toBe(200);
spyonOne.mockRestore();
})
test("Should return status 401 to indicate Unauthorized user",async() =>{
const loggedInUser ={
email:"[email protected]",
password:`1234560`,
}
const spyonOne = jest.spyOn(User,"findOne").mockResolvedValueOnce({
//@ts-ignore
email:"[email protected]",
password:"$2a$10$zonhxs7J7h3ls7nv781Xoea1X4dJoui3t0/7gRPR3Ek4Rgzg6RAQe",
})
const response = await request(app).post("/api/v1/users/login")
.send(loggedInUser)
expect(response.status).toBe(401);
spyonOne.mockRestore();
})
});
12 changes: 11 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
"author": "atlp",
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.0.0",
"@types/bcrypt": "^5.0.2",
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
Expand All @@ -29,20 +32,27 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^28.2.0",
"eslint-plugin-react": "^7.34.1",
"globals": "^15.0.0",
"jest": "^29.7.0",
"nodemon": "^3.1.0",
"prettier": "^3.2.5",
"sequelize-cli": "^6.6.2",
"supertest": "^6.3.4",
"ts-jest": "^29.1.2",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
"typescript": "^5.4.5",
"typescript-eslint": "^7.7.0"
},
"dependencies": {
"@types/jsonwebtoken": "^9.0.6",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"jsonwebtoken": "^9.0.2",
"path": "^0.12.7",
"pg": "^8.11.5",
"pg-hstore": "^2.3.4",
Expand Down
29 changes: 29 additions & 0 deletions src/controllers/userControllers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { Request, Response } from "express";
import * as userService from "../services/user.service";
import { generateToken } from "../utils/jsonwebtoken";
import { comparePasswords } from "../utils/passwordCopare";
import { loggedInUser} from "../services/user.service";

export const fetchAllUsers = async (req: Request, res: Response) => {
try {
Expand All @@ -25,3 +28,29 @@ export const fetchAllUsers = async (req: Request, res: Response) => {
});
}
};

export const userLogin = async(req:Request,res:Response) =>{
const {email, password} = req.body;
const user = await loggedInUser(email);
const accessToken = await generateToken(user);
if(!user){
res.status(404).json({
status:404,
message:'User Not Found ! Please Register new ancount'
});
}else{
const match = await comparePasswords(password,user.password);
if(!match){
res.status(401).json({
status:401,
message:' User email or password is incorrect!'
});
}else{
res.status(200).json({
status:200,
message:"Logged in",
token:accessToken
})
}
}
}
6 changes: 5 additions & 1 deletion src/docs/swagger.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express from "express";
import { serve, setup } from "swagger-ui-express";
import { env } from "../utils/env";
import { getUsers, userSchema } from "./users";
import { getUsers,loginAsUser, userSchema,loginSchema } from "./users";

const docRouter = express.Router();

Expand Down Expand Up @@ -30,12 +30,16 @@ const options = {
paths: {
"/api/v1/users": {
get: getUsers
},
"/api/v1/users/login":{
post:loginAsUser
}
},

components: {
schemas: {
User: userSchema,
login:loginSchema
},
securitySchemes: {
bearerAuth: {
Expand Down
44 changes: 44 additions & 0 deletions src/docs/users.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { response } from "express"

export const userSchema = {
type: "object",
properties: {
Expand All @@ -16,6 +18,18 @@ export const userSchema = {
},
},
}
export const loginSchema = {
type:"object",
properties:{
email: {
type: "string",
format: "email",
},
password: {
type: "string",
},
}
}

export const getUsers = {
tags: ["Users"],
Expand All @@ -35,4 +49,34 @@ export const getUsers = {
},
},
},
}

export const loginAsUser = {
tags: ["Users"],
summary: "Login as user",
requestBody:{
required:true,
content:{
"application/json":{
schema:{
$ref: "#/components/schemas/login",
}
}
}
},
responses:{
200:{
description:"OK",
content:{
"application/json":{
schema:{
type:"array",
items:{
$ref: "#/components/schemas/login",
}
}
}
}
}
}
}
6 changes: 6 additions & 0 deletions src/helpers/hashPassword.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import bcrypt from 'bcryptjs'

export const hashedPassword = async(password: string) => {
const hashpass = await bcrypt.hash(password, 10)
return hashpass;
}
20 changes: 20 additions & 0 deletions src/middleware/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Request,Response,NextFunction } from "express";
import jwt,{sign,verify} from "jsonwebtoken";
import { env } from "../utils/env";


export const isLoggedIn = (req:Request,res:Response,next:NextFunction) =>{
const accessToken = req.headers.authorization?.split(' ')[1];
if(accessToken){
jwt.verify(accessToken,`${env.jwt_secret}`,(error:any,user:any) =>{
if(error){
return res.status(401).json({message:'Unauthorized! Login first'})
}else{
req.body = user;
next()
}
})
}else{
return res.status(401).json({message:'Unauthorized! Login first'})
}
}
3 changes: 2 additions & 1 deletion src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Router } from "express";
import { fetchAllUsers } from "../controllers/userControllers";
import { fetchAllUsers,userLogin } from "../controllers/userControllers";

const userRoutes = Router();

userRoutes.get("/", fetchAllUsers);
userRoutes.post('/login',userLogin);

export default userRoutes;
19 changes: 19 additions & 0 deletions src/sequelize/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,32 @@ module.exports = {
development: {
url: process.env.DB_CONNECTION,
dialect: "postgres",
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false,
},
},
},
test: {
url: process.env.TEST_DB,
dialect: "postgres",
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false,
},
},
},
production: {
url: process.env.DB_CONNECTION,
dialect: "postgres",
dialectOptions: {
ssl: {
require: true,
rejectUnauthorized: false,
},
},
},

};
10 changes: 0 additions & 10 deletions src/sequelize/migrations/20240412153905-delete-isMerchant.js

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,6 @@ module.exports = {
type: Sequelize.STRING,
allowNull: false,
},
isMerchant: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
Expand All @@ -45,4 +41,4 @@ module.exports = {
async down(queryInterface, Sequelize) {
await queryInterface.describeTable("users");
},
};
};
44 changes: 44 additions & 0 deletions src/sequelize/migrations/20240418083442-UserTests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @type {import('sequelize-cli').Migration} */
module.exports = {
async up(queryInterface, Sequelize) {
await queryInterface.createTable("usersTests", {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
name: {
type: Sequelize.STRING,
allowNull: false,
},
username: {
type: Sequelize.STRING,
allowNull: false,
},
email: {
type: Sequelize.STRING,
allowNull: false,
validate: {
isEmail: true,
},
},
password: {
type: Sequelize.STRING,
allowNull: false,
},
createdAt: {
allowNull: false,
type: Sequelize.DATE,
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE,
},
});
},

async down(queryInterface, Sequelize) {
await queryInterface.describeTable("usersTests");
},
};
2 changes: 1 addition & 1 deletion src/sequelize/models/user.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DataTypes } from "sequelize";
import sequelize from "../../config/dbConnection";

const User = sequelize.define("User", {
const User = sequelize.define("user", {
name: {
type: DataTypes.STRING,
allowNull: false,
Expand Down
Loading
Loading