Skip to content

Commit

Permalink
fix(user-metrics): implement endpoint for retrieving user data (#145)
Browse files Browse the repository at this point in the history
- implement endpoint for retrieving buyer and seller data
- implement endpoint for retrieving category metrics

[Fixes #140]
  • Loading branch information
jkarenzi authored Jul 11, 2024
1 parent 680ae6f commit 88c23e7
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 4 deletions.
13 changes: 12 additions & 1 deletion src/__test__/category.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import request from 'supertest';
import app from '../app';
import { afterAllHook, beforeAllHook, getVendorToken } from './testSetup';
import { afterAllHook, beforeAllHook, getAdminToken, getVendorToken } from './testSetup';

beforeAll(beforeAllHook);
afterAll(afterAllHook);

describe('Category Creation Tests', () => {
beforeAll(async () => {
token = await getVendorToken();
adminToken = await getAdminToken()
});
let token: string;
let categoryId: number;
let adminToken: string;

it('should create a new category with valid data', async () => {
const categoryData = {
Expand Down Expand Up @@ -203,4 +205,13 @@ describe('Category Creation Tests', () => {
expect(response.status).toBe(404);
expect(response.body.message).toBe('Category Not Found');
});

it('should return an array of category metrics', async () => {
const response = await request(app)
.get('/api/v1/category/get_metrics')
.set('Authorization', `Bearer ${adminToken}`)

expect(response.status).toBe(200)
expect(response.body.data).toBeDefined()
})
});
16 changes: 16 additions & 0 deletions src/__test__/userController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { afterAllHook, beforeAllHook } from './testSetup';
import jwt from 'jsonwebtoken';
import dbConnection from '../database';
import UserModel from '../database/models/userModel';
import { getAdminToken } from './testSetup';
const userRepository = dbConnection.getRepository(UserModel);

beforeAll(beforeAllHook);
Expand Down Expand Up @@ -495,3 +496,18 @@ if (user) {
}
});
});

describe('User metrics tests', () => {
let adminToken:string;
beforeAll(async() => {
adminToken = await getAdminToken()
})
it('should get user metrics successfully', async () => {
const response = await request(app).get('/api/v1/user/get_metrics')
.set('Authorization', `Bearer ${adminToken}`)

expect(response.status).toBe(200)
expect(response.body.buyerData).toBeDefined()
expect(response.body.vendorData).toBeDefined()
})
})
67 changes: 67 additions & 0 deletions src/controller/categoryController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import dbConnection from '../database';
import Category from '../database/models/categoryEntity';
import { check, validationResult } from 'express-validator';
import errorHandler from '../middlewares/errorHandler';
import { Order } from '../database/models/orderEntity';

const categoryRepository = dbConnection.getRepository(Category);
const orderRepository = dbConnection.getRepository(Order)

interface categoryRequestBody {
name: string;
Expand Down Expand Up @@ -130,3 +132,68 @@ export const deleteCategory = errorHandler(
res.status(200).json({ message: 'Category deleted successfully' });
}
);

export const getCategoryMetrics = errorHandler(
async (req: Request, res: Response) => {
const orders = await orderRepository.find({
where:{
paid: true
},
select:{
id:true,
totalAmount:true,
paid:true,
orderDetails:{
id:true,
price:true,
quantity:true,
product:{
id:true,
name:true,
category:{
id:true,
name:true
}
},
}
},
relations:['orderDetails','orderDetails.product','orderDetails.product.category']
})

const categories = await categoryRepository.find({
select:{
products:{
id:true
}
},
relations: ['products']
})

const counter:{[key:string]:number} = {};
for(const order of orders){
for(const orderDetail of order.orderDetails){
if(orderDetail.product.category.name in order){
counter[orderDetail.product.category.name] += orderDetail.price
}else{
counter[orderDetail.product.category.name] = orderDetail.price
}
}
}

const data = []

for(const category of categories){
if(category.name in counter){
data.push({
categoryName: category.name,
totalProducts: category.products.length,
totalSales: counter[category.name]
})
}
}

data.sort((a, b) => b.totalSales - a.totalSales)

return res.status(200).json({data:data.slice(0,4)})
}
);
18 changes: 18 additions & 0 deletions src/controller/userController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,3 +413,21 @@ export const removeProfileImg = errorHandler(
});
}
);

export const getUserMetrics = errorHandler(async (req: Request, res: Response) => {
const users = await userRepository.find({relations:['userType']});

const buyerData: number[] = Array(12).fill(0);
const vendorData: number[] = Array(12).fill(0);

for(const user of users){
const monthIndex = user.createdAt.getMonth()
if(user.userType.name === 'Buyer'){
buyerData[monthIndex] += 1
}else if(user.userType.name === 'Vendor'){
vendorData[monthIndex] += 1
}
}

return res.status(200).json({buyerData, vendorData})
})
8 changes: 8 additions & 0 deletions src/database/models/userModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import {
Column,
ManyToOne,
OneToMany,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { Role } from './roleEntity';
import { Order } from './orderEntity';
Expand Down Expand Up @@ -54,6 +56,12 @@ export default class UserModel {
@Column({ nullable: true })
twoFactorCode: number;

@CreateDateColumn()
createdAt: Date;

@UpdateDateColumn()
updatedAt: Date;

constructor(user: Partial<UserModel>) {
Object.assign(this, user);
}
Expand Down
19 changes: 19 additions & 0 deletions src/docs/categoryDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,22 @@
* '500':
* description: Internal Server Error
*/


/**
* @swagger
* /api/v1/category/get_metrics:
* get:
* summary: Get category metrics
* tags: [User]
* security:
* - bearerAuth: []
* responses:
* '200':
* description: Successful
* '401':
* description: Unauthorized
* '500':
* description: Internal Server Error
*/

17 changes: 17 additions & 0 deletions src/docs/userRegisterDocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -399,3 +399,20 @@
* '500':
* description: Internal Server Error
*/

/**
* @swagger
* /api/v1/user/get_metrics:
* get:
* summary: Get buyer and vendor metrics
* tags: [User]
* security:
* - bearerAuth: []
* responses:
* '200':
* description: Successful
* '401':
* description: Unauthorized
* '500':
* description: Internal Server Error
*/
2 changes: 1 addition & 1 deletion src/emails/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ async function sendEmail(emailType: EmailType, recipient: string, data: Data) {
}
}

export default sendEmail;
export default sendEmail;
7 changes: 6 additions & 1 deletion src/routes/categoryRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import {
deleteCategory,
getAllCategories,
getCategory,
getCategoryMetrics,
updateCategory,
} from '../controller/categoryController';
import { IsLoggedIn } from '../middlewares/isLoggedIn';
import { checkRole } from '../middlewares/authorize';

const categoryRouter = Router();

categoryRouter.route('/get_metrics').get(IsLoggedIn, checkRole(['Admin']), getCategoryMetrics)

categoryRouter
.route('/')
.post(IsLoggedIn, createCategory)
Expand All @@ -20,4 +24,5 @@ categoryRouter
.put(IsLoggedIn, updateCategory)
.delete(IsLoggedIn, deleteCategory);

export default categoryRouter;

export default categoryRouter;
4 changes: 3 additions & 1 deletion src/routes/userRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
updateProfile,
deleteUser,
changeProfileImg,
removeProfileImg
removeProfileImg,
getUserMetrics
} from '../controller/userController';

import {
Expand Down Expand Up @@ -56,4 +57,5 @@ userRouter.post('/subscribe', subscribe);
userRouter.get('/subscribe/delete/:id', removeSubscriber);
userRouter.get('/subscribe/getAll', getAllSubscriber);
userRouter.route('/profileImg').patch(IsLoggedIn, upload.fields([{name:'image'}]), changeProfileImg).delete(IsLoggedIn, removeProfileImg)
userRouter.get('/get_metrics', IsLoggedIn, checkRole(['Admin']), getUserMetrics)
export default userRouter;

0 comments on commit 88c23e7

Please sign in to comment.