Skip to content

Commit

Permalink
ft(login) user can log into the E-commerce application via email & pa…
Browse files Browse the repository at this point in the history
…ssword

        -user login with email and password and return jwt as result of
        successifull login
        -unit test for user login endpont both successifull and unsuccessfull login event
        -swagger documentation for user login endpoint

        [Finishes #187419169]
  • Loading branch information
niyobertin committed Apr 19, 2024
2 parents 7002dc7 + f554f6b commit eb3ddb0
Show file tree
Hide file tree
Showing 22 changed files with 412 additions and 57 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
uses: codecov/[email protected]
with:
token: ${{ secrets.CODECOV_TOKEN }}
slug: soleil00/eagles-ec-be
slug: atlp-rwanda/eagles-ec-be

- name: Trigger Render Deployment
env:
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/node_modules
/.env
/package-lock.json
/package-lock.json
/coverage
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div style="display: flex; justify-content: center;" align="center">
<img src="https://codecov.io/gh/soleil00/eagles-ec-be/branch/dev/graph/badge.svg?token=9c1e8e93-1062-4e49-a58d-b2777a75fb70" alt="Codecov" >
<img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/soleil00/eagles-ec-be/test.yml" >
<img alt="GitHub Actions Workflow Status" src="https://img.shields.io/github/actions/workflow/status/atlp-rwanda/eagles-ec-be/deploy.yml" >
<a href="https://codeclimate.com/github/atlp-rwanda/eagles-ec-be/maintainability"><img src="https://api.codeclimate.com/v1/badges/dfe8454356fb9da65407/maintainability" /></a>

</div>
Expand Down
59 changes: 59 additions & 0 deletions __test__/user.route.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import request from 'supertest';
import { beforeAll, afterAll, beforeEach, afterEach, test } from '@jest/globals';
import app from '../src/utils/server';
import User from '../src/sequelize/models/users';
import sequelize, { connect } from '../src/config/dbConnection';


describe('Testing User route', () => {
beforeAll(async () => {
try { await
connect();
}
catch (error) { sequelize.close(); } }, 20000);

afterAll(async () => {
await sequelize.close(); });
beforeEach(async () => {
await User.destroy({ truncate: true });
});

test('should return 201 and create a new user when registering successfully', async () => {
const userData = {
name: 'yvanna',
username: 'testuser',
email: '[email protected]',
password: 'test1234',
};
const response = await request(app)
.post('/api/v1/users')
.send(userData);
expect(response.status).toBe(201); }, 20000);

test('should return 409 when registering with an existing email', async () => { await User.create({
name: 'yvanna',
username: 'testuser',
email: '[email protected]',
password: 'test1234',
});

const userData = {
name: 'yvanna',
username: 'testuser',
email: '[email protected]',
password: 'test1234',
};

const response = await request(app)
.post('/api/v1/users')
.send(userData);
expect(response.status).toBe(409); }, 20000);

test('should return 500 when registering with an invalid credential', async () => {
const userData = {
email: '[email protected]', name: "", username: 'existinguser', };
const response = await request(app)
.post('/api/v1/users')
.send(userData);

expect(response.status).toBe(500); }, 20000); });
2 changes: 1 addition & 1 deletion __test__/user.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import request from "supertest";
import { beforeAll, afterAll, jest, test } from "@jest/globals";
import app from "../src/utils/server";
import User from "../src/sequelize/models/user";
import User from "../src/sequelize/models/users";
import * as userServices from "../src/services/user.service";
import sequelize, { connect } from "../src/config/dbConnection";
import {env} from "../src/utils/env";
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"author": "atlp",
"license": "MIT",
"devDependencies": {
"@eslint/js": "^9.0.0",
"@types/bcryptjs": "^2.4.6",
"@types/cors": "^2.8.17",
"@types/dotenv": "^8.2.0",
"@types/express": "^4.17.21",
Expand All @@ -31,18 +33,21 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-jest": "^28.2.0",
"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/bcrypt": "^5.0.2",
"bcrypt": "^5.1.1",
"bcryptjs": "^2.4.3",
"cors": "^2.8.5",
"cross-env": "^7.0.3",
"dotenv": "^16.4.5",
Expand Down
5 changes: 4 additions & 1 deletion src/config/dbConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import { env } from "../utils/env";

const envT = process.env.NODE_ENV;

const sequelize = new Sequelize(envT === "test" ? env.test_db_url : env.db_url);
const sequelize = new Sequelize(envT === "test" ? env.test_db_url : env.db_url,{
dialect: 'postgres',
})


export const connect = async () => {
try {
Expand Down
23 changes: 23 additions & 0 deletions src/controllers/userControllers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as userService from "../services/user.service";
import { generateToken } from "../utils/jsonwebtoken";
import { comparePasswords } from "../helpers/comparePassword";
import { loggedInUser} from "../services/user.service";
import { createUserService, getUserByEmail } from "../services/user.service";

export const fetchAllUsers = async (req: Request, res: Response) => {
try {
Expand Down Expand Up @@ -55,3 +56,25 @@ export const userLogin = async(req:Request,res:Response) =>{
};
};


export const createUserController = async (req: Request, res: Response) => {
try {
const { name, email, username, password } = req.body;
const user = await createUserService(name, email, username, password);
if (!user) {
return res.status(409).json({
status: 409,
message: 'User already exists' });
}
res.status(201).json({
status: 201,
message: "User successfully created."
});

} catch (err: any) {
if (err.name === 'UnauthorizedError' && err.message === 'User already exists') {
return res.status(409).json({ error: 'User already exists' });
}
res.status(500).json({ error: err });
}
};
17 changes: 14 additions & 3 deletions src/docs/swagger.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import express from "express";
import { serve, setup } from "swagger-ui-express";
import { env } from "../utils/env";
import { getUsers, userSchema } from "./users";
import {
createUsers,
getUsers,
loginAsUser,
userSchema,
loginSchema
} from "./users";

const docRouter = express.Router();

Expand Down Expand Up @@ -29,13 +35,18 @@ const options = {

paths: {
"/api/v1/users": {
get: getUsers
}
get: getUsers,
post: createUsers
},
"/api/v1/users/login": {
post: loginAsUser
},
},

components: {
schemas: {
User: userSchema,
Login:loginSchema,
},
securitySchemes: {
bearerAuth: {
Expand Down
68 changes: 67 additions & 1 deletion 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 @@ -17,6 +19,18 @@ export const userSchema = {
},
}

export const loginSchema ={
properties :{
email: {
type: "string",
format: "email",
},
password: {
type: "string",
},
}
}

export const getUsers = {
tags: ["Users"],
summary: "Get all users",
Expand All @@ -35,4 +49,56 @@ export const getUsers = {
},
},
},
}
}

export const createUsers = {

tags: ["Users"],
summary: "Register a new user",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
$ref: "#/components/schemas/User",
},
},
},
},
responses: {
201: {
description: "Created",
content: {
"application/json": {
schema: {
$ref: "#/components/schemas/User",
},
},
},
},
400: {
description: "Bad request",
},
},
}

export const loginAsUser ={
tags: ["Users"],
summary: "Login as user",
requestBody: {
required: true,
content: {
"application/json": {
schema: {
$ref: "#/components/schemas/Login"
}
}
}
},
responses: {
200: {
description: "OK",
}
}
};

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;
}
2 changes: 2 additions & 0 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Request, Response, Router } from "express";
import userRoutes from "./userRoutes";


const appROutes = Router();

appROutes.use("/users", userRoutes);


export default appROutes;
8 changes: 7 additions & 1 deletion src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { Router } from "express";
import { fetchAllUsers,userLogin } from "../controllers/userControllers";
import {
fetchAllUsers,
createUserController,
userLogin }
from "../controllers/userControllers";

const userRoutes = Router();

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


export default userRoutes;
7 changes: 7 additions & 0 deletions src/sequelize/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,12 @@ module.exports = {
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");
},
};
};
Loading

0 comments on commit eb3ddb0

Please sign in to comment.