-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Jiin Kim
authored and
Jiin Kim
committed
Jan 13, 2025
1 parent
12bbcc0
commit ee021e5
Showing
5 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
paths: | ||
/student/delete-account: | ||
delete: | ||
summary: Delete User Account | ||
description: Deletes the authenticated user's account from the system. | ||
security: | ||
- BearerAuth: [] | ||
parameters: | ||
- in: header | ||
name: Authorization | ||
required: true | ||
description: Bearer token for authentication | ||
schema: | ||
type: string | ||
pattern: '^Bearer\s[\w-]+\.[\w-]+\.[\w-]+$' | ||
example: 'Bearer eyJhbGciOiJIUzI1NiIs...' | ||
responses: | ||
'200': | ||
description: User successfully deleted | ||
content: | ||
application/json: | ||
schema: | ||
type: object | ||
properties: | ||
message: | ||
type: string | ||
example: "User successfully deleted" | ||
'401': | ||
description: Unauthorized - Invalid or missing access token | ||
content: | ||
application/json: | ||
schema: | ||
type: object | ||
properties: | ||
error: | ||
type: string | ||
example: "Invalid or expired access token" | ||
'500': | ||
description: Internal server error | ||
content: | ||
application/json: | ||
schema: | ||
type: object | ||
properties: | ||
error: | ||
type: string | ||
example: "Failed to delete user" | ||
components: | ||
securitySchemes: | ||
BearerAuth: | ||
type: http | ||
scheme: bearer | ||
bearerFormat: JWT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { APIGatewayProxyHandler } from 'aws-lambda'; | ||
import { successResponse, errorResponse, wrapHandler } from '../handlerUtil'; | ||
import { getUserDetailsFromAccessToken } from '../../service/cognito'; | ||
import { deleteUserFromCognito } from '../../service/cognito'; | ||
|
||
const deleteUser: APIGatewayProxyHandler = async event => { | ||
try { | ||
// Get access token from Authorization header | ||
const authHeader = event.headers.Authorization || event.headers.authorization; | ||
if (!authHeader) { | ||
return errorResponse('Authorization header is required', 401); | ||
} | ||
|
||
// Extract the token (remove "Bearer " if present) | ||
const accessToken = authHeader.replace(/^Bearer\s/, ''); | ||
|
||
try { | ||
// Verify access token and get user details | ||
const userDetails = await getUserDetailsFromAccessToken(accessToken); | ||
// Delete user from Cognito | ||
await deleteUserFromCognito(userDetails.username); | ||
|
||
return successResponse({ | ||
message: 'User successfully deleted', | ||
}); | ||
} catch (error) { | ||
if (error instanceof Error && error.message === 'Access token is invalid or has expired.') { | ||
return errorResponse('Invalid or expired access token', 401); | ||
} | ||
throw error; | ||
} | ||
} catch (error) { | ||
return errorResponse(error instanceof Error ? error.message : 'Failed to delete user', 500); | ||
} | ||
}; | ||
|
||
export const handler = wrapHandler(deleteUser); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { APIGatewayProxyEvent, Context, Callback } from 'aws-lambda'; | ||
import { handler } from '../../../src/lambda/handlers/deleteUser'; | ||
import { getUserDetailsFromAccessToken, deleteUserFromCognito } from '../../../src/service/cognito'; | ||
import * as handlerUtil from '../../../src/lambda/handlerUtil'; | ||
|
||
jest.mock('../../../src/service/cognito'); | ||
|
||
describe('deleteUser', () => { | ||
const mockUserDetails = { | ||
username: 'testuser', | ||
email: '[email protected]', | ||
}; | ||
|
||
const invokeHandler = async (event: Partial<APIGatewayProxyEvent>) => { | ||
const context = {} as Context; | ||
const callback = {} as Callback; | ||
const defaultEvent = { | ||
headers: {}, | ||
queryStringParameters: {}, | ||
}; | ||
return handler({ ...defaultEvent, ...event } as APIGatewayProxyEvent, context, callback); | ||
}; | ||
|
||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
(getUserDetailsFromAccessToken as jest.Mock).mockResolvedValue(mockUserDetails); | ||
(deleteUserFromCognito as jest.Mock).mockResolvedValue(undefined); | ||
jest.spyOn(handlerUtil, 'successResponse'); | ||
jest.spyOn(handlerUtil, 'errorResponse'); | ||
}); | ||
|
||
it('should call errorResponse when authorization header is missing', async () => { | ||
await invokeHandler({}); | ||
expect(handlerUtil.errorResponse).toHaveBeenCalledWith('Authorization header is required', 401); | ||
}); | ||
|
||
it('should call errorResponse when access token is invalid', async () => { | ||
(getUserDetailsFromAccessToken as jest.Mock).mockRejectedValue(new Error('Access token is invalid or has expired.')); | ||
await invokeHandler({ | ||
headers: { Authorization: 'Bearer invalid_token' }, | ||
}); | ||
expect(handlerUtil.errorResponse).toHaveBeenCalledWith('Invalid or expired access token', 401); | ||
}); | ||
|
||
it('should delete user successfully with valid token', async () => { | ||
await invokeHandler({ | ||
headers: { Authorization: 'Bearer valid_token' }, | ||
}); | ||
expect(getUserDetailsFromAccessToken).toHaveBeenCalledWith('valid_token'); | ||
expect(deleteUserFromCognito).toHaveBeenCalledWith(mockUserDetails.username); | ||
expect(handlerUtil.successResponse).toHaveBeenCalledWith({ | ||
message: 'User successfully deleted', | ||
}); | ||
}); | ||
|
||
it('should handle unexpected errors during deletion', async () => { | ||
(deleteUserFromCognito as jest.Mock).mockRejectedValue(new Error('Unexpected error')); | ||
await invokeHandler({ | ||
headers: { Authorization: 'Bearer valid_token' }, | ||
}); | ||
expect(handlerUtil.errorResponse).toHaveBeenCalledWith('Unexpected error', 500); | ||
}); | ||
}); |