From aa7b964e8bb889a8df00d72dd82c466e3da49ec3 Mon Sep 17 00:00:00 2001 From: Siva Date: Tue, 5 Aug 2025 23:28:56 -0400 Subject: [PATCH 01/14] team leave end point --- src/functions/teams/leave/handler.ts | 146 +++++++++++++++++++++++++++ src/functions/teams/leave/index.ts | 22 ++++ src/functions/teams/leave/schema.ts | 9 ++ 3 files changed, 177 insertions(+) create mode 100644 src/functions/teams/leave/handler.ts create mode 100644 src/functions/teams/leave/index.ts create mode 100644 src/functions/teams/leave/schema.ts diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts new file mode 100644 index 0000000..aa89b9a --- /dev/null +++ b/src/functions/teams/leave/handler.ts @@ -0,0 +1,146 @@ +import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway'; +import { middyfy } from '@libs/lambda'; +import schema from './schema'; +import { MongoDB, validateToken, UserDoc, TeamDoc } from '../../../util'; +import * as path from 'path'; +import * as dotenv from 'dotenv'; +//import fetch from 'node-fetch'; + +dotenv.config({ path: path.resolve(process.cwd(), '.env') }); + + +const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (event) => { + try{ + + const {auth_token, auth_email, team_id} = event.body; + + // 1. Validate auth token + const tokenValid = validateToken(auth_token, process.env.JWT_SECRET, auth_email); + if(!tokenValid){ + return{ + statusCode:401, + body: JSON.stringify({statusCode:401, message: 'Unauthorized'}) + } + } + + // 2. connect to mongoDB + const db = MongoDB.getInstance(process.env.MONGO_URI); + await db.connect(); + const users = db.getCollection('users'); + const teams = db.getCollection('teams'); + + // 3. check if user exisits + const authUser = await users.findOne({email:auth_email}) + if(!authUser){ + return{ + statusCode: 404, + body: JSON.stringify({statusCode:404, message: 'Auth user not found'}) + } + } + + // 4. check if team exisits + const team = await teams.findOne({team_id:team_id}) + if(!team){ + return{ + statusCode:404, + body: JSON.stringify({statusCode:404, message: 'Team not found'}) + } + } + + // 5. Check if team is disbanded + if(team.status == "Disbanded"){ + return{ + statusCode:400, + body: JSON.stringify({statusCode:400, message: "Team already disbanded"}) + } + } + + // 6. No team members + if(team.members.length == 0){ + return{ + statusCode: 400, + body: JSON.stringify({statusCode:400, message: 'Empty team member list'}) + } + } + + // 7. Check if user is in team + if (!team.members.includes(authUser.email)) { + return{ + statusCode: 400, + body: JSON.stringify({statusCode:400, message: 'User not in team'}) + } + } + + //grabs team info object + const teamInfo = authUser.team_info + + // 8. Check if user is team lead + if(teamInfo.role == "leader"){ + const response = await fetch("http://localhost:3000/teams/disband",{ + method: "POST", + headers:{ + "Content-Type": "application/json" + }, + body: JSON.stringify( + { + auth_token: auth_token, + auth_email: auth_email, + team_id: team_id + } + ) + }) + + const data = await response.json() as { message: string }; + + return{ + statusCode: response.status, + body: JSON.stringify({statusCode:response.status, message:data.message}) + } + } + + + // 9. Remove user from team + await teams.updateOne( + { team_id: team_id, members: authUser.email}, + { + $pull:{ + members: authUser.email + } + } + ) + + + // 10. clear team_info and set confirmed_team + authUser.confirmed_team = false + teamInfo.team_id = "" + teamInfo.role = "" + teamInfo.pending_invites = [] + + // 11. update the mondoDB user + await users.updateOne( + { email: auth_email}, + { + $set:{ + confirmed_team: authUser.confirmed_team, + team_info: authUser.team_info + } + } + ) + + return { + statusCode:200, + body: JSON.stringify({"message": "Successfully left team"}) + } + + } + + catch (error){ + console.error('Error deleting team member:', error); + return { + statusCode: 500, + body: JSON.stringify({ statusCode: 500, message: 'Internal server error' }), + }; + } +} + +export const main = middyfy(teamLeave); diff --git a/src/functions/teams/leave/index.ts b/src/functions/teams/leave/index.ts new file mode 100644 index 0000000..0cd1a6e --- /dev/null +++ b/src/functions/teams/leave/index.ts @@ -0,0 +1,22 @@ +import { handlerPath } from '@libs/handler-resolver'; +import schema from './schema'; + +export default { + handler: `${handlerPath(__dirname)}/handler.main`, + events: [ + { + http: { + method: 'post', + path: 'teams/leave', + cors: true, + request: { + schemas: { + 'application/json': schema, + }, + }, + }, + }, + ], +}; + + diff --git a/src/functions/teams/leave/schema.ts b/src/functions/teams/leave/schema.ts new file mode 100644 index 0000000..8936cdf --- /dev/null +++ b/src/functions/teams/leave/schema.ts @@ -0,0 +1,9 @@ +export default { + type: 'object', + properties: { + auth_token: { type: 'string' }, + auth_email: { type: 'string', format: 'email' }, + team_id: {type:"string"} + }, + required: ['auth_token', 'auth_email', 'team_id'], +} as const; From 45e8dbe365dc8eb07908b6217a34261728cd40e5 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 6 Aug 2025 22:51:27 -0400 Subject: [PATCH 02/14] test cases and updated leave logic --- src/functions/teams/leave/handler.ts | 35 ++--- tests/teams-leave.test.ts | 214 +++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 25 deletions(-) create mode 100644 tests/teams-leave.test.ts diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index aa89b9a..2d2e6aa 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -1,9 +1,10 @@ import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway'; import { middyfy } from '@libs/lambda'; import schema from './schema'; -import { MongoDB, validateToken, UserDoc, TeamDoc } from '../../../util'; +import { MongoDB, validateToken, UserDoc, TeamDoc, teamsDisband } from '../../../util'; //change to actual disband import * as path from 'path'; import * as dotenv from 'dotenv'; + //import fetch from 'node-fetch'; dotenv.config({ path: path.resolve(process.cwd(), '.env') }); @@ -64,7 +65,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even } // 7. Check if user is in team - if (!team.members.includes(authUser.email)) { + if (!team.members.includes(auth_email)) { return{ statusCode: 400, body: JSON.stringify({statusCode:400, message: 'User not in team'}) @@ -76,26 +77,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even // 8. Check if user is team lead if(teamInfo.role == "leader"){ - const response = await fetch("http://localhost:3000/teams/disband",{ - method: "POST", - headers:{ - "Content-Type": "application/json" - }, - body: JSON.stringify( - { - auth_token: auth_token, - auth_email: auth_email, - team_id: team_id - } - ) - }) - - const data = await response.json() as { message: string }; - - return{ - statusCode: response.status, - body: JSON.stringify({statusCode:response.status, message:data.message}) - } + return await teamsDisband(auth_token, auth_email, team_id); //mocked function replace with right funtion name } @@ -111,10 +93,13 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even // 10. clear team_info and set confirmed_team + authUser.confirmed_team = false - teamInfo.team_id = "" - teamInfo.role = "" - teamInfo.pending_invites = [] + authUser.team_info = { + team_id: "", + role: "", + pending_invites: [] + }; // 11. update the mondoDB user await users.updateOne( diff --git a/tests/teams-leave.test.ts b/tests/teams-leave.test.ts new file mode 100644 index 0000000..4bc4c50 --- /dev/null +++ b/tests/teams-leave.test.ts @@ -0,0 +1,214 @@ +import { main } from '../src/functions/teams/leave/handler'; +import { validateToken, MongoDB, teamsDisband} from '../src/util'; +import { createEvent, mockContext } from './helper'; + +jest.mock('../src/util'); + +describe('teamLeave Lambda',()=>{ + const path = '/teams/leave'; + const httpMethod = 'POST'; + let mockUsers: any; + let mockTeams: any; + + beforeEach(()=>{ + + //mock validToken + (validateToken as jest.Mock).mockReturnValue(true); + + (teamsDisband as unknown as jest.Mock) = jest.fn(); + + (teamsDisband as jest.Mock).mockResolvedValue({ + statusCode: 200, + body: JSON.stringify({ statusCode: 200, message: "Mocked up function" }), + }); + + + //mock user info + mockUsers = + { + findOne: jest.fn(), + updateOne: jest.fn() + } + + mockTeams = { + findOne: jest.fn(), + updateOne: jest.fn() + }; + + // Mock MongoDB.getInstance and collection access + (MongoDB.getInstance as jest.Mock).mockReturnValue({ + connect: jest.fn(), + getCollection: (name: string) => { + if (name === 'users') return mockUsers; + if (name === 'teams') return mockTeams; + } + }); + + }); + + // 1. check for invalid volidToken + it('returns 401 if token is invalid', async () => { + const userData = { + auth_token: 'token', + auth_email: 'user@example.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + (validateToken as jest.Mock).mockReturnValue(false); + + const result = await main(mockEvent, mockContext, mockCallback); + + expect(result.statusCode).toBe(401); + expect(JSON.parse(result.body).message).toBe('Unauthorized'); + }); + + + // 3. Disbanded team + it('return 400 disbanded team', async() => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue( + { + confirmed_team: false, + team_info:{ + team_id: "team1234", + role: "leader", + pending_invites: [{},{}] + } + } + ); + mockTeams.findOne.mockResolvedValue( + { + team_id: "team1234", + leader_email: "leader@gmail.com", + members: [], + status: "Disbanded", + created: "dateCreated", + updated: "dateUpdated" + } + ) + const result = await main(mockEvent, mockContext, mockCallback) + expect(result.statusCode).toBe(400); + expect(JSON.parse(result.body).message).toBe("Team already disbanded"); + }) + + //empty team + it('return 400 empty team', async() => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue( + { + confirmed_team: false, + team_info:{ + team_id: "team1234", + role: "leader", + pending_invites: [{},{}] + } + } + ); + mockTeams.findOne.mockResolvedValue( + { + team_id: "team1234", + leader_email: "leader@gmail.com", + members: [], + status: "Active", + created: "dateCreated", + updated: "dateUpdated" + } + ) + const result = await main(mockEvent, mockContext, mockCallback) + expect(result.statusCode).toBe(400); + expect(JSON.parse(result.body).message).toBe('Empty team member list'); + }) + + // not in the team + it('return 400 user not in team', async() => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue( + { + confirmed_team: false, + team_info:{ + team_id: "team1234", + role: "leader", + pending_invites: [{},{}] + } + } + ); + mockTeams.findOne.mockResolvedValue( + { + team_id: "team1234", + leader_email: "leader@gmail.com", + members: ["member1@gmail.com, member2@gmail.com"], + status: "Active", + created: "dateCreated", + updated: "dateUpdated" + } + ) + const result = await main(mockEvent, mockContext, mockCallback) + expect(result.statusCode).toBe(400); + expect(JSON.parse(result.body).message).toBe('User not in team'); + }) + + + // team leader leaves + it('return 200 team lead leaves', async() => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue( + { + confirmed_team: false, + team_info:{ + team_id: "team1234", + role: "leader", + pending_invites: [{},{}] + } + } + ); + mockTeams.findOne.mockResolvedValue( + { + team_id: "team1234", + leader_email: "leader@gmail.com", + members: ["leader@gmail.com"], + status: "Active", + created: "dateCreated", + updated: "dateUpdated" + } + ) + const result = await main(mockEvent, mockContext, mockCallback) + expect(result.statusCode).toBe(200); + expect(JSON.parse(result.body).message).toBe("Mocked up function"); + }) + +}) From e1245aeed2a43585255dd00536eac9ef8e71be51 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 6 Aug 2025 23:02:13 -0400 Subject: [PATCH 03/14] add one more case --- tests/teams-leave.test.ts | 50 +++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/tests/teams-leave.test.ts b/tests/teams-leave.test.ts index 4bc4c50..fb147ef 100644 --- a/tests/teams-leave.test.ts +++ b/tests/teams-leave.test.ts @@ -12,7 +12,7 @@ describe('teamLeave Lambda',()=>{ beforeEach(()=>{ - //mock validToken + // mock validToken fail (validateToken as jest.Mock).mockReturnValue(true); (teamsDisband as unknown as jest.Mock) = jest.fn(); @@ -23,7 +23,7 @@ describe('teamLeave Lambda',()=>{ }); - //mock user info + // mock user info mockUsers = { findOne: jest.fn(), @@ -46,7 +46,7 @@ describe('teamLeave Lambda',()=>{ }); - // 1. check for invalid volidToken + // Case 1: invalid volidToken it('returns 401 if token is invalid', async () => { const userData = { auth_token: 'token', @@ -66,7 +66,7 @@ describe('teamLeave Lambda',()=>{ }); - // 3. Disbanded team + // Case 2: Team already Disbanded it('return 400 disbanded team', async() => { const userData = { auth_token: 'token', @@ -102,7 +102,7 @@ describe('teamLeave Lambda',()=>{ expect(JSON.parse(result.body).message).toBe("Team already disbanded"); }) - //empty team + // Case 3: memeber list is empty it('return 400 empty team', async() => { const userData = { auth_token: 'token', @@ -138,7 +138,7 @@ describe('teamLeave Lambda',()=>{ expect(JSON.parse(result.body).message).toBe('Empty team member list'); }) - // not in the team + // Case 4: member not in the team it('return 400 user not in team', async() => { const userData = { auth_token: 'token', @@ -175,7 +175,7 @@ describe('teamLeave Lambda',()=>{ }) - // team leader leaves + // Case 5: team lead leaves it('return 200 team lead leaves', async() => { const userData = { auth_token: 'token', @@ -211,4 +211,40 @@ describe('teamLeave Lambda',()=>{ expect(JSON.parse(result.body).message).toBe("Mocked up function"); }) + // Case 6: Success + it('return 200 success', async() => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue( + { + confirmed_team: false, + team_info:{ + team_id: "team1234", + role: "member", + pending_invites: [{},{}] + } + } + ); + mockTeams.findOne.mockResolvedValue( + { + team_id: "team1234", + leader_email: "leader@gmail.com", + members: ["leader@gmail.com"], + status: "Active", + created: "dateCreated", + updated: "dateUpdated" + } + ) + const result = await main(mockEvent, mockContext, mockCallback) + expect(result.statusCode).toBe(200); + expect(JSON.parse(result.body).message).toBe("Successfully left team"); + }) + }) From 78e0bc6fb36f79115e8b2215c5be72ed76efdf16 Mon Sep 17 00:00:00 2001 From: Siva Date: Mon, 18 Aug 2025 23:26:55 -0400 Subject: [PATCH 04/14] fixed to include disband endpoint --- serverless.ts | 2 + src/functions/teams/leave/handler.ts | 128 ++++---- src/functions/teams/leave/index.ts | 2 - src/functions/teams/leave/schema.ts | 2 +- tests/teams-leave.test.ts | 454 +++++++++++++-------------- 5 files changed, 277 insertions(+), 311 deletions(-) diff --git a/serverless.ts b/serverless.ts index e609c25..f682165 100644 --- a/serverless.ts +++ b/serverless.ts @@ -20,6 +20,7 @@ import verifyEmail from '@functions/verify-email'; import deleteUser from '@functions/delete'; import userExists from '@functions/user-exists'; import interestForm from '@functions/interest-form'; +import teamLeave from '@functions/teams/leave'; import * as path from 'path'; import * as dotenv from 'dotenv'; @@ -66,6 +67,7 @@ const serverlessConfiguration: AWS = { deleteUser, userExists, interestForm, + teamLeave }, package: { individually: true, patterns: ['!.env*', '.env.vault'] }, custom: { diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index 2d2e6aa..f56e509 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -1,7 +1,8 @@ import type { ValidatedEventAPIGatewayProxyEvent } from '@libs/api-gateway'; import { middyfy } from '@libs/lambda'; import schema from './schema'; -import { MongoDB, validateToken, UserDoc, TeamDoc, teamsDisband } from '../../../util'; //change to actual disband +import { UserDocument, TeamDocument } from '../../../types'; +import { MongoDB, validateToken, disbandTeam } from '../../../util'; //change to actual disband import * as path from 'path'; import * as dotenv from 'dotenv'; @@ -9,123 +10,116 @@ import * as dotenv from 'dotenv'; dotenv.config({ path: path.resolve(process.cwd(), '.env') }); - const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (event) => { - try{ - - const {auth_token, auth_email, team_id} = event.body; + try { + const { auth_token, auth_email, team_id } = event.body; // 1. Validate auth token const tokenValid = validateToken(auth_token, process.env.JWT_SECRET, auth_email); - if(!tokenValid){ - return{ - statusCode:401, - body: JSON.stringify({statusCode:401, message: 'Unauthorized'}) - } + if (!tokenValid) { + return { + statusCode: 401, + body: JSON.stringify({ statusCode: 401, message: 'Unauthorized' }), + }; } // 2. connect to mongoDB - const db = MongoDB.getInstance(process.env.MONGO_URI); + const db = MongoDB.getInstance(process.env.MONGO_URI); await db.connect(); - const users = db.getCollection('users'); - const teams = db.getCollection('teams'); + const users = db.getCollection('users'); + const teams = db.getCollection('teams'); - // 3. check if user exisits - const authUser = await users.findOne({email:auth_email}) - if(!authUser){ - return{ + // 3. check if user exisits + const authUser = await users.findOne({ email: auth_email }); + if (!authUser) { + return { statusCode: 404, - body: JSON.stringify({statusCode:404, message: 'Auth user not found'}) - } + body: JSON.stringify({ statusCode: 404, message: 'Auth user not found' }), + }; } // 4. check if team exisits - const team = await teams.findOne({team_id:team_id}) - if(!team){ - return{ - statusCode:404, - body: JSON.stringify({statusCode:404, message: 'Team not found'}) - } + const team = await teams.findOne({ team_id: team_id }); + if (!team) { + return { + statusCode: 404, + body: JSON.stringify({ statusCode: 404, message: 'Team not found' }), + }; } // 5. Check if team is disbanded - if(team.status == "Disbanded"){ - return{ - statusCode:400, - body: JSON.stringify({statusCode:400, message: "Team already disbanded"}) - } + if (team.status == 'Disbanded') { + return { + statusCode: 400, + body: JSON.stringify({ statusCode: 400, message: 'Team already disbanded' }), + }; } // 6. No team members - if(team.members.length == 0){ - return{ + if (team.members.length == 0) { + return { statusCode: 400, - body: JSON.stringify({statusCode:400, message: 'Empty team member list'}) - } + body: JSON.stringify({ statusCode: 400, message: 'Empty team member list' }), + }; } - + // 7. Check if user is in team if (!team.members.includes(auth_email)) { - return{ + return { statusCode: 400, - body: JSON.stringify({statusCode:400, message: 'User not in team'}) - } + body: JSON.stringify({ statusCode: 400, message: 'User not in team' }), + }; } //grabs team info object - const teamInfo = authUser.team_info + const teamInfo = authUser.team_info; // 8. Check if user is team lead - if(teamInfo.role == "leader"){ - return await teamsDisband(auth_token, auth_email, team_id); //mocked function replace with right funtion name + if (teamInfo.role == 'leader') { + return await disbandTeam(auth_token, auth_email, team_id); } - // 9. Remove user from team await teams.updateOne( - { team_id: team_id, members: authUser.email}, + { team_id: team_id, members: authUser.email }, { - $pull:{ - members: authUser.email - } + $pull: { + members: authUser.email, + }, } - ) - + ); // 10. clear team_info and set confirmed_team - authUser.confirmed_team = false + authUser.confirmed_team = false; authUser.team_info = { - team_id: "", - role: "", - pending_invites: [] + team_id: '', + role: null, + pending_invites: [], }; - // 11. update the mondoDB user + // 11. update the MongoDB user await users.updateOne( - { email: auth_email}, + { email: auth_email }, { - $set:{ + $set: { confirmed_team: authUser.confirmed_team, - team_info: authUser.team_info - } + team_info: authUser.team_info, + }, } - ) - - return { - statusCode:200, - body: JSON.stringify({"message": "Successfully left team"}) - } + ); - } - - catch (error){ + return { + statusCode: 200, + body: JSON.stringify({ message: 'Successfully left team' }), + }; + } catch (error) { console.error('Error deleting team member:', error); return { statusCode: 500, body: JSON.stringify({ statusCode: 500, message: 'Internal server error' }), }; } -} +}; export const main = middyfy(teamLeave); diff --git a/src/functions/teams/leave/index.ts b/src/functions/teams/leave/index.ts index 0cd1a6e..ec36806 100644 --- a/src/functions/teams/leave/index.ts +++ b/src/functions/teams/leave/index.ts @@ -18,5 +18,3 @@ export default { }, ], }; - - diff --git a/src/functions/teams/leave/schema.ts b/src/functions/teams/leave/schema.ts index 8936cdf..249de03 100644 --- a/src/functions/teams/leave/schema.ts +++ b/src/functions/teams/leave/schema.ts @@ -3,7 +3,7 @@ export default { properties: { auth_token: { type: 'string' }, auth_email: { type: 'string', format: 'email' }, - team_id: {type:"string"} + team_id: { type: 'string' }, }, required: ['auth_token', 'auth_email', 'team_id'], } as const; diff --git a/tests/teams-leave.test.ts b/tests/teams-leave.test.ts index fb147ef..a2e7478 100644 --- a/tests/teams-leave.test.ts +++ b/tests/teams-leave.test.ts @@ -1,250 +1,222 @@ import { main } from '../src/functions/teams/leave/handler'; -import { validateToken, MongoDB, teamsDisband} from '../src/util'; +import { validateToken, MongoDB, disbandTeam } from '../src/util'; import { createEvent, mockContext } from './helper'; jest.mock('../src/util'); -describe('teamLeave Lambda',()=>{ - const path = '/teams/leave'; - const httpMethod = 'POST'; - let mockUsers: any; - let mockTeams: any; - - beforeEach(()=>{ - - // mock validToken fail - (validateToken as jest.Mock).mockReturnValue(true); - - (teamsDisband as unknown as jest.Mock) = jest.fn(); - - (teamsDisband as jest.Mock).mockResolvedValue({ - statusCode: 200, - body: JSON.stringify({ statusCode: 200, message: "Mocked up function" }), - }); - - - // mock user info - mockUsers = - { - findOne: jest.fn(), - updateOne: jest.fn() - } - - mockTeams = { - findOne: jest.fn(), - updateOne: jest.fn() - }; - - // Mock MongoDB.getInstance and collection access - (MongoDB.getInstance as jest.Mock).mockReturnValue({ - connect: jest.fn(), - getCollection: (name: string) => { - if (name === 'users') return mockUsers; - if (name === 'teams') return mockTeams; - } - }); +describe('teamLeave Lambda', () => { + const path = '/teams/leave'; + const httpMethod = 'POST'; + let mockUsers: any; + let mockTeams: any; - }); + beforeEach(() => { + (validateToken as jest.Mock).mockReturnValue(true); - // Case 1: invalid volidToken - it('returns 401 if token is invalid', async () => { - const userData = { - auth_token: 'token', - auth_email: 'user@example.com', - team_id: 'team123', - }; - - const mockEvent = createEvent(userData, path, httpMethod); - const mockCallback = jest.fn(); - - (validateToken as jest.Mock).mockReturnValue(false); - - const result = await main(mockEvent, mockContext, mockCallback); - - expect(result.statusCode).toBe(401); - expect(JSON.parse(result.body).message).toBe('Unauthorized'); - }); + (disbandTeam as jest.Mock) = jest.fn(); + (disbandTeam as jest.Mock).mockResolvedValue({ + statusCode: 200, + body: JSON.stringify({ statusCode: 200, message: 'Mocked up function' }), + }); - // Case 2: Team already Disbanded - it('return 400 disbanded team', async() => { - const userData = { - auth_token: 'token', - auth_email: 'leader@gmail.com', - team_id: 'team123', - }; - - const mockEvent = createEvent(userData, path, httpMethod); - const mockCallback = jest.fn(); - - mockUsers.findOne.mockResolvedValue( - { - confirmed_team: false, - team_info:{ - team_id: "team1234", - role: "leader", - pending_invites: [{},{}] - } - } - ); - mockTeams.findOne.mockResolvedValue( - { - team_id: "team1234", - leader_email: "leader@gmail.com", - members: [], - status: "Disbanded", - created: "dateCreated", - updated: "dateUpdated" - } - ) - const result = await main(mockEvent, mockContext, mockCallback) - expect(result.statusCode).toBe(400); - expect(JSON.parse(result.body).message).toBe("Team already disbanded"); - }) - - // Case 3: memeber list is empty - it('return 400 empty team', async() => { - const userData = { - auth_token: 'token', - auth_email: 'leader@gmail.com', - team_id: 'team123', - }; - - const mockEvent = createEvent(userData, path, httpMethod); - const mockCallback = jest.fn(); - - mockUsers.findOne.mockResolvedValue( - { - confirmed_team: false, - team_info:{ - team_id: "team1234", - role: "leader", - pending_invites: [{},{}] - } - } - ); - mockTeams.findOne.mockResolvedValue( - { - team_id: "team1234", - leader_email: "leader@gmail.com", - members: [], - status: "Active", - created: "dateCreated", - updated: "dateUpdated" - } - ) - const result = await main(mockEvent, mockContext, mockCallback) - expect(result.statusCode).toBe(400); - expect(JSON.parse(result.body).message).toBe('Empty team member list'); - }) - - // Case 4: member not in the team - it('return 400 user not in team', async() => { - const userData = { - auth_token: 'token', - auth_email: 'leader@gmail.com', - team_id: 'team123', - }; - - const mockEvent = createEvent(userData, path, httpMethod); - const mockCallback = jest.fn(); - - mockUsers.findOne.mockResolvedValue( - { - confirmed_team: false, - team_info:{ - team_id: "team1234", - role: "leader", - pending_invites: [{},{}] - } - } - ); - mockTeams.findOne.mockResolvedValue( - { - team_id: "team1234", - leader_email: "leader@gmail.com", - members: ["member1@gmail.com, member2@gmail.com"], - status: "Active", - created: "dateCreated", - updated: "dateUpdated" - } - ) - const result = await main(mockEvent, mockContext, mockCallback) - expect(result.statusCode).toBe(400); - expect(JSON.parse(result.body).message).toBe('User not in team'); - }) - - - // Case 5: team lead leaves - it('return 200 team lead leaves', async() => { - const userData = { - auth_token: 'token', - auth_email: 'leader@gmail.com', - team_id: 'team123', - }; - - const mockEvent = createEvent(userData, path, httpMethod); - const mockCallback = jest.fn(); - - mockUsers.findOne.mockResolvedValue( - { - confirmed_team: false, - team_info:{ - team_id: "team1234", - role: "leader", - pending_invites: [{},{}] - } - } - ); - mockTeams.findOne.mockResolvedValue( - { - team_id: "team1234", - leader_email: "leader@gmail.com", - members: ["leader@gmail.com"], - status: "Active", - created: "dateCreated", - updated: "dateUpdated" - } - ) - const result = await main(mockEvent, mockContext, mockCallback) - expect(result.statusCode).toBe(200); - expect(JSON.parse(result.body).message).toBe("Mocked up function"); - }) - - // Case 6: Success - it('return 200 success', async() => { - const userData = { - auth_token: 'token', - auth_email: 'leader@gmail.com', - team_id: 'team123', - }; - - const mockEvent = createEvent(userData, path, httpMethod); - const mockCallback = jest.fn(); - - mockUsers.findOne.mockResolvedValue( - { - confirmed_team: false, - team_info:{ - team_id: "team1234", - role: "member", - pending_invites: [{},{}] - } - } - ); - mockTeams.findOne.mockResolvedValue( - { - team_id: "team1234", - leader_email: "leader@gmail.com", - members: ["leader@gmail.com"], - status: "Active", - created: "dateCreated", - updated: "dateUpdated" - } - ) - const result = await main(mockEvent, mockContext, mockCallback) - expect(result.statusCode).toBe(200); - expect(JSON.parse(result.body).message).toBe("Successfully left team"); - }) - -}) + // mock user info + mockUsers = { + findOne: jest.fn(), + updateOne: jest.fn(), + }; + + mockTeams = { + findOne: jest.fn(), + updateOne: jest.fn(), + }; + + // Mock MongoDB.getInstance and collection access + (MongoDB.getInstance as jest.Mock).mockReturnValue({ + connect: jest.fn(), + getCollection: (name: string) => { + if (name === 'users') return mockUsers; + if (name === 'teams') return mockTeams; + }, + }); + }); + + // Case 1: invalid volidToken + it('returns 401 if token is invalid', async () => { + const userData = { + auth_token: 'token', + auth_email: 'user@example.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + (validateToken as jest.Mock).mockReturnValue(false); + + const result = await main(mockEvent, mockContext, mockCallback); + + expect(result.statusCode).toBe(401); + expect(JSON.parse(result.body).message).toBe('Unauthorized'); + }); + + // Case 2: Team already Disbanded + it('return 400 disbanded team', async () => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue({ + confirmed_team: false, + team_info: { + team_id: 'team1234', + role: 'leader', + pending_invites: [{}, {}], + }, + }); + mockTeams.findOne.mockResolvedValue({ + team_id: 'team1234', + leader_email: 'leader@gmail.com', + members: [], + status: 'Disbanded', + created: 'dateCreated', + updated: 'dateUpdated', + }); + const result = await main(mockEvent, mockContext, mockCallback); + expect(result.statusCode).toBe(400); + expect(JSON.parse(result.body).message).toBe('Team already disbanded'); + }); + + // Case 3: memeber list is empty + it('return 400 empty team', async () => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue({ + confirmed_team: false, + team_info: { + team_id: 'team1234', + role: 'leader', + pending_invites: [{}, {}], + }, + }); + mockTeams.findOne.mockResolvedValue({ + team_id: 'team1234', + leader_email: 'leader@gmail.com', + members: [], + status: 'Active', + created: 'dateCreated', + updated: 'dateUpdated', + }); + const result = await main(mockEvent, mockContext, mockCallback); + expect(result.statusCode).toBe(400); + expect(JSON.parse(result.body).message).toBe('Empty team member list'); + }); + + // Case 4: member not in the team + it('return 400 user not in team', async () => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue({ + confirmed_team: false, + team_info: { + team_id: 'team1234', + role: 'leader', + pending_invites: [{}, {}], + }, + }); + mockTeams.findOne.mockResolvedValue({ + team_id: 'team1234', + leader_email: 'leader@gmail.com', + members: ['member1@gmail.com', 'member2@gmail.com'], + status: 'Active', + created: 'dateCreated', + updated: 'dateUpdated', + }); + const result = await main(mockEvent, mockContext, mockCallback); + expect(result.statusCode).toBe(400); + expect(JSON.parse(result.body).message).toBe('User not in team'); + }); + + // Case 5: team lead leaves + it('return 200 team lead leaves', async () => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue({ + confirmed_team: false, + team_info: { + team_id: 'team1234', + role: 'leader', + pending_invites: [{}, {}], + }, + }); + mockTeams.findOne.mockResolvedValue({ + team_id: 'team1234', + leader_email: 'leader@gmail.com', + members: ['leader@gmail.com'], + status: 'Active', + created: 'dateCreated', + updated: 'dateUpdated', + }); + const result = await main(mockEvent, mockContext, mockCallback); + expect(result.statusCode).toBe(200); + expect(JSON.parse(result.body).message).toBe('Mocked up function'); + }); + + // Case 6: Success + it('return 200 success', async () => { + const userData = { + auth_token: 'token', + auth_email: 'leader@gmail.com', + team_id: 'team123', + }; + + const mockEvent = createEvent(userData, path, httpMethod); + const mockCallback = jest.fn(); + + mockUsers.findOne.mockResolvedValue({ + confirmed_team: false, + team_info: { + team_id: 'team1234', + role: 'member', + pending_invites: [{}, {}], + }, + }); + mockTeams.findOne.mockResolvedValue({ + team_id: 'team1234', + leader_email: 'leader@gmail.com', + members: ['leader@gmail.com'], + status: 'Active', + created: 'dateCreated', + updated: 'dateUpdated', + }); + const result = await main(mockEvent, mockContext, mockCallback); + expect(result.statusCode).toBe(200); + expect(JSON.parse(result.body).message).toBe('Successfully left team'); + }); +}); From 2f9e603b2ddf923d1c58c0e6606501cff6e36a6b Mon Sep 17 00:00:00 2001 From: Siva Date: Mon, 18 Aug 2025 23:32:42 -0400 Subject: [PATCH 05/14] fixed serverless.ts --- serverless.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/serverless.ts b/serverless.ts index f682165..67705d1 100644 --- a/serverless.ts +++ b/serverless.ts @@ -9,6 +9,7 @@ import discord from '@functions/discord'; import read from '@functions/read'; import waiver from '@functions/waiver'; import resume from '@functions/resume'; +import teamsInvite from '@functions/teams/invite'; import resetPassword from '@functions/reset-password'; import forgotPassword from '@functions/forgot-password'; import leaderboard from '@functions/leaderboard'; @@ -20,6 +21,12 @@ import verifyEmail from '@functions/verify-email'; import deleteUser from '@functions/delete'; import userExists from '@functions/user-exists'; import interestForm from '@functions/interest-form'; +import teamsCreate from '@functions/teams/create'; +import memberRemoval from '@functions/teams/member-removal'; +import declineInvitation from '@functions/teams/decline-invite'; +import teamsJoin from '@functions/teams/join'; +import teamsRead from '@functions/teams/read'; +import disband from '@functions/teams/disband'; import teamLeave from '@functions/teams/leave'; import * as path from 'path'; @@ -54,6 +61,7 @@ const serverlessConfiguration: AWS = { attendEvent, waiver, resume, + teamsInvite, read, discord, forgotPassword, @@ -67,6 +75,12 @@ const serverlessConfiguration: AWS = { deleteUser, userExists, interestForm, + teamsCreate, + memberRemoval, + declineInvitation, + teamsJoin, + teamsRead, + disband, teamLeave }, package: { individually: true, patterns: ['!.env*', '.env.vault'] }, @@ -85,3 +99,6 @@ const serverlessConfiguration: AWS = { }; module.exports = serverlessConfiguration; + + + From e4f7ba829e72e0f0c82e2a919f45fd9c01dbb6b5 Mon Sep 17 00:00:00 2001 From: Siva Date: Tue, 19 Aug 2025 00:32:32 -0400 Subject: [PATCH 06/14] serverless.ts prettier --- serverless.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/serverless.ts b/serverless.ts index 801ce6a..062442d 100644 --- a/serverless.ts +++ b/serverless.ts @@ -81,8 +81,7 @@ const serverlessConfiguration: AWS = { teamsJoin, teamsRead, disband, - teamLeave - + teamLeave, }, package: { individually: true, patterns: ['!.env*', '.env.vault'] }, custom: { @@ -100,6 +99,3 @@ const serverlessConfiguration: AWS = { }; module.exports = serverlessConfiguration; - - - From a60009d33fb4734e2cbe005f62d15717de0ea30a Mon Sep 17 00:00:00 2001 From: Siva Date: Tue, 19 Aug 2025 00:37:30 -0400 Subject: [PATCH 07/14] minor fixes --- src/functions/teams/leave/handler.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index f56e509..5592c99 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -75,9 +75,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even const teamInfo = authUser.team_info; // 8. Check if user is team lead - if (teamInfo.role == 'leader') { - return await disbandTeam(auth_token, auth_email, team_id); - } + if (teamInfo.role == 'leader') return await disbandTeam(auth_token, auth_email, team_id); // 9. Remove user from team await teams.updateOne( From c41193a6861d6c3869ec73bc3ccd75702d70934a Mon Sep 17 00:00:00 2001 From: Siva Date: Tue, 19 Aug 2025 00:40:24 -0400 Subject: [PATCH 08/14] typescript error fix --- tests/teams-leave.test.ts | 56 +++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/tests/teams-leave.test.ts b/tests/teams-leave.test.ts index a2e7478..b90e111 100644 --- a/tests/teams-leave.test.ts +++ b/tests/teams-leave.test.ts @@ -1,5 +1,6 @@ import { main } from '../src/functions/teams/leave/handler'; import { validateToken, MongoDB, disbandTeam } from '../src/util'; +import { UserDocument, TeamDocument } from '../src/types'; import { createEvent, mockContext } from './helper'; jest.mock('../src/util'); @@ -7,8 +8,14 @@ jest.mock('../src/util'); describe('teamLeave Lambda', () => { const path = '/teams/leave'; const httpMethod = 'POST'; - let mockUsers: any; - let mockTeams: any; + + type MockCollection = { + findOne: jest.Mock | null>, any>; + updateOne: jest.Mock, any>; + }; + + let mockUsers: MockCollection; + let mockTeams: MockCollection; beforeEach(() => { (validateToken as jest.Mock).mockReturnValue(true); @@ -76,7 +83,10 @@ describe('teamLeave Lambda', () => { team_info: { team_id: 'team1234', role: 'leader', - pending_invites: [{}, {}], + pending_invites: [ + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + ], }, }); mockTeams.findOne.mockResolvedValue({ @@ -84,8 +94,8 @@ describe('teamLeave Lambda', () => { leader_email: 'leader@gmail.com', members: [], status: 'Disbanded', - created: 'dateCreated', - updated: 'dateUpdated', + created: new Date(), + updated: new Date(), }); const result = await main(mockEvent, mockContext, mockCallback); expect(result.statusCode).toBe(400); @@ -108,7 +118,10 @@ describe('teamLeave Lambda', () => { team_info: { team_id: 'team1234', role: 'leader', - pending_invites: [{}, {}], + pending_invites: [ + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + ], }, }); mockTeams.findOne.mockResolvedValue({ @@ -116,8 +129,8 @@ describe('teamLeave Lambda', () => { leader_email: 'leader@gmail.com', members: [], status: 'Active', - created: 'dateCreated', - updated: 'dateUpdated', + created: new Date(), + updated: new Date(), }); const result = await main(mockEvent, mockContext, mockCallback); expect(result.statusCode).toBe(400); @@ -140,7 +153,10 @@ describe('teamLeave Lambda', () => { team_info: { team_id: 'team1234', role: 'leader', - pending_invites: [{}, {}], + pending_invites: [ + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + ], }, }); mockTeams.findOne.mockResolvedValue({ @@ -148,8 +164,8 @@ describe('teamLeave Lambda', () => { leader_email: 'leader@gmail.com', members: ['member1@gmail.com', 'member2@gmail.com'], status: 'Active', - created: 'dateCreated', - updated: 'dateUpdated', + created: new Date(), + updated: new Date(), }); const result = await main(mockEvent, mockContext, mockCallback); expect(result.statusCode).toBe(400); @@ -172,7 +188,10 @@ describe('teamLeave Lambda', () => { team_info: { team_id: 'team1234', role: 'leader', - pending_invites: [{}, {}], + pending_invites: [ + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + ], }, }); mockTeams.findOne.mockResolvedValue({ @@ -180,8 +199,8 @@ describe('teamLeave Lambda', () => { leader_email: 'leader@gmail.com', members: ['leader@gmail.com'], status: 'Active', - created: 'dateCreated', - updated: 'dateUpdated', + created: new Date(), + updated: new Date(), }); const result = await main(mockEvent, mockContext, mockCallback); expect(result.statusCode).toBe(200); @@ -204,7 +223,10 @@ describe('teamLeave Lambda', () => { team_info: { team_id: 'team1234', role: 'member', - pending_invites: [{}, {}], + pending_invites: [ + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + { team_id: 'fake-team', invited_by: 'test@example.com', invited_at: new Date(), team_name: 'Fake Team' }, + ], }, }); mockTeams.findOne.mockResolvedValue({ @@ -212,8 +234,8 @@ describe('teamLeave Lambda', () => { leader_email: 'leader@gmail.com', members: ['leader@gmail.com'], status: 'Active', - created: 'dateCreated', - updated: 'dateUpdated', + created: new Date(), + updated: new Date(), }); const result = await main(mockEvent, mockContext, mockCallback); expect(result.statusCode).toBe(200); From 79ad216e94af188841b676e74b10f0118e0022c1 Mon Sep 17 00:00:00 2001 From: Siva Date: Tue, 19 Aug 2025 00:43:55 -0400 Subject: [PATCH 09/14] typescript error fix --- tests/teams-leave.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/teams-leave.test.ts b/tests/teams-leave.test.ts index b90e111..aa96c23 100644 --- a/tests/teams-leave.test.ts +++ b/tests/teams-leave.test.ts @@ -9,10 +9,10 @@ describe('teamLeave Lambda', () => { const path = '/teams/leave'; const httpMethod = 'POST'; - type MockCollection = { - findOne: jest.Mock | null>, any>; - updateOne: jest.Mock, any>; - }; + interface MockCollection { + findOne: jest.Mock | null>, unknown[]>; + updateOne: jest.Mock, unknown[]>; + } let mockUsers: MockCollection; let mockTeams: MockCollection; From 05eaa6bd3687beae96c0480e90ef01e7c9629363 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 20 Aug 2025 22:36:00 -0400 Subject: [PATCH 10/14] name change --- src/functions/teams/leave/handler.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index 5592c99..dcecda2 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -12,10 +12,10 @@ dotenv.config({ path: path.resolve(process.cwd(), '.env') }); const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (event) => { try { - const { auth_token, auth_email, team_id } = event.body; + const { auth_token: authToken, auth_email: authEmail, team_id: teamId } = event.body; // 1. Validate auth token - const tokenValid = validateToken(auth_token, process.env.JWT_SECRET, auth_email); + const tokenValid = validateToken(authToken, process.env.JWT_SECRET, authEmail); if (!tokenValid) { return { statusCode: 401, @@ -30,7 +30,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even const teams = db.getCollection('teams'); // 3. check if user exisits - const authUser = await users.findOne({ email: auth_email }); + const authUser = await users.findOne({ email: authEmail }); if (!authUser) { return { statusCode: 404, @@ -39,7 +39,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even } // 4. check if team exisits - const team = await teams.findOne({ team_id: team_id }); + const team = await teams.findOne({ team_id: teamId }); if (!team) { return { statusCode: 404, @@ -64,7 +64,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even } // 7. Check if user is in team - if (!team.members.includes(auth_email)) { + if (!team.members.includes(authEmail)) { return { statusCode: 400, body: JSON.stringify({ statusCode: 400, message: 'User not in team' }), @@ -75,11 +75,12 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even const teamInfo = authUser.team_info; // 8. Check if user is team lead - if (teamInfo.role == 'leader') return await disbandTeam(auth_token, auth_email, team_id); + if (teamInfo.role == 'leader') return await disbandTeam(authToken, authEmail, teamId); + // 9. Remove user from team await teams.updateOne( - { team_id: team_id, members: authUser.email }, + { team_id: teamId, members: authUser.email }, { $pull: { members: authUser.email, @@ -98,7 +99,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even // 11. update the MongoDB user await users.updateOne( - { email: auth_email }, + { email: authEmail }, { $set: { confirmed_team: authUser.confirmed_team, From b2c44ac09dbe4800eaf912d647c0043f1067bf73 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 20 Aug 2025 22:38:41 -0400 Subject: [PATCH 11/14] prettier --- src/functions/teams/leave/handler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index dcecda2..f7ff385 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -76,7 +76,6 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even // 8. Check if user is team lead if (teamInfo.role == 'leader') return await disbandTeam(authToken, authEmail, teamId); - // 9. Remove user from team await teams.updateOne( From 6b4f24cc5630f06894e7f4311fdc17883377ccd4 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 3 Sep 2025 09:14:29 -0400 Subject: [PATCH 12/14] fixed lead logic --- src/functions/teams/leave/handler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index f7ff385..72f7427 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -56,7 +56,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even } // 6. No team members - if (team.members.length == 0) { + if (team.members.length + 1 == 0) { return { statusCode: 400, body: JSON.stringify({ statusCode: 400, message: 'Empty team member list' }), @@ -64,7 +64,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even } // 7. Check if user is in team - if (!team.members.includes(authEmail)) { + if (!team.members.includes(authEmail) && team.leader_email !== authEmail) { return { statusCode: 400, body: JSON.stringify({ statusCode: 400, message: 'User not in team' }), From 73187573ec5c72d2b4e2ba46e755fa4fd3efbdf0 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 3 Sep 2025 14:33:05 -0400 Subject: [PATCH 13/14] valid team lead check --- src/functions/teams/leave/handler.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index 72f7427..0801902 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -55,6 +55,15 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even }; } + //7. Check is teamlead is real + const teamAuth = await users.findOne({ email: team.leader_email }); + if(!teamAuth){ + return{ + statusCode: 400, + body: JSON.stringify({ statusCode: 400, message: 'Invalid team lead' }), + } + } + // 6. No team members if (team.members.length + 1 == 0) { return { From b2d6008ac50ecc91a4f75dcb19052ad38f992245 Mon Sep 17 00:00:00 2001 From: Siva Date: Wed, 3 Sep 2025 14:34:58 -0400 Subject: [PATCH 14/14] valid team lead check --- src/functions/teams/leave/handler.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/functions/teams/leave/handler.ts b/src/functions/teams/leave/handler.ts index 0801902..6ac441a 100644 --- a/src/functions/teams/leave/handler.ts +++ b/src/functions/teams/leave/handler.ts @@ -55,16 +55,16 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even }; } - //7. Check is teamlead is real + // 6. Check is teamlead is real const teamAuth = await users.findOne({ email: team.leader_email }); - if(!teamAuth){ - return{ + if (!teamAuth) { + return { statusCode: 400, body: JSON.stringify({ statusCode: 400, message: 'Invalid team lead' }), - } + }; } - // 6. No team members + // 7. No team members if (team.members.length + 1 == 0) { return { statusCode: 400, @@ -72,7 +72,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even }; } - // 7. Check if user is in team + // 8. Check if user is in team if (!team.members.includes(authEmail) && team.leader_email !== authEmail) { return { statusCode: 400, @@ -83,10 +83,10 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even //grabs team info object const teamInfo = authUser.team_info; - // 8. Check if user is team lead + // 9. Check if user is team lead if (teamInfo.role == 'leader') return await disbandTeam(authToken, authEmail, teamId); - // 9. Remove user from team + // 10. Remove user from team await teams.updateOne( { team_id: teamId, members: authUser.email }, { @@ -96,7 +96,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even } ); - // 10. clear team_info and set confirmed_team + // 11. clear team_info and set confirmed_team authUser.confirmed_team = false; authUser.team_info = { @@ -105,7 +105,7 @@ const teamLeave: ValidatedEventAPIGatewayProxyEvent = async (even pending_invites: [], }; - // 11. update the MongoDB user + // 12. update the MongoDB user await users.updateOne( { email: authEmail }, {