Skip to content

Commit

Permalink
feat: refactored participant request (team, member) service
Browse files Browse the repository at this point in the history
  • Loading branch information
navneethkrish committed Nov 3, 2024
1 parent d634aa5 commit 5f74020
Show file tree
Hide file tree
Showing 24 changed files with 2,733 additions and 2,059 deletions.
173 changes: 0 additions & 173 deletions apps/web-api/src/admin/admin.controller.ts

This file was deleted.

26 changes: 7 additions & 19 deletions apps/web-api/src/admin/admin.module.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,16 @@
/* eslint-disable prettier/prettier */
import { CacheModule, Module } from '@nestjs/common';

import { ParticipantsRequestService } from '../participants-request/participants-request.service';
import { LocationTransferService } from '../utils/location-transfer/location-transfer.service';
import { AwsService } from '../utils/aws/aws.service';
import { RedisService } from '../utils/redis/redis.service';
import { SlackService } from '../utils/slack/slack.service';
import { AdminService } from './admin.service';
import { JwtService } from '../utils/jwt/jwt.service';
import { AdminController } from './admin.controller';
import { ForestAdminService } from '../utils/forest-admin/forest-admin.service';

import { ParticipantsRequestModule } from '../participants-request/participants-request.module';
import { SharedModule } from '../shared/shared.module';
import { AdminParticipantsRequestController } from './participants-request.controller';
import { AdminAuthController } from './auth.controller';
@Module({
imports: [CacheModule.register()],
controllers: [AdminController],
imports: [CacheModule.register(), ParticipantsRequestModule, SharedModule],
controllers: [AdminParticipantsRequestController, AdminAuthController],
providers: [
ParticipantsRequestService,
LocationTransferService,
AwsService,
RedisService,
SlackService,
AdminService,
JwtService,
ForestAdminService,
JwtService
],
})
export class AdminModule {}
41 changes: 28 additions & 13 deletions apps/web-api/src/admin/admin.service.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,41 @@
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '../utils/jwt/jwt.service';
import { LogService } from '../shared/log.service';

@Injectable()
export class AdminService {
constructor(private readonly jwtService: JwtService) {}
constructor(
private readonly jwtService: JwtService,
private logger: LogService,
) {}

/**
* Validates given username and password for the admin against the env config and
* creates a signed jwt token if credentials are valid else, throws {@link UnauthorizedException}
* @param username admin username
* @param password admin password
* @returns a signed jwt token in json format {code: 1, accessToken: <token>} if crdentials valid
* @throws UnauthorizedException if credentials not valid
* Logs in the admin using the provided username and password.
* Validates credentials from environment variables.
* @param username - Admin username
* @param password - Admin password
* @returns Object containing access token on successful login
* @throws UnauthorizedException if credentials are invalid
*/
async signIn(username: string, password: string): Promise<any> {
const usernameFromEnv = process.env.ADMIN_USERNAME;
const passwordFromEnv = process.env.ADMIN_PASSWORD;

if (username !== usernameFromEnv || passwordFromEnv !== password) {
throw new UnauthorizedException();
async login(username: string, password: string): Promise<{ code:Number, accessToken: string }> {
if (!this.isValidAdminCredentials(username, password)) {
this.logger.error('Invalid credentials provided for admin login.');
throw new UnauthorizedException('Invalid credentials');
}
this.logger.info('Generating admin access token...');
const accessToken = await this.jwtService.getSignedToken(['DIRECTORYADMIN']);
return { code: 1, accessToken: accessToken };
}

/**
* Validates the provided credentials against stored environment variables.
* @param username - Input username
* @param password - Input password
* @returns Boolean indicating if credentials are valid
*/
private isValidAdminCredentials(username: string, password: string): boolean {
const validUsername = process.env.ADMIN_USERNAME;
const validPassword = process.env.ADMIN_PASSWORD;
return username === validUsername && password === validPassword;
}
}
22 changes: 22 additions & 0 deletions apps/web-api/src/admin/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Body, Controller, Post, UsePipes } from '@nestjs/common';
import { LoginRequestDto } from 'libs/contracts/src/schema';
import { ZodValidationPipe } from 'nestjs-zod';
import { AdminService } from './admin.service';

@Controller('v1/admin/auth')
export class AdminAuthController {
constructor(private readonly adminService: AdminService) {}

/**
* Handles admin login requests.
* Validates the request body against the LoginRequestDto schema.
* @param loginRequestDto - The login request data transfer object
* @returns Access token if login is successful
*/
@Post('login')
@UsePipes(ZodValidationPipe)
async login(@Body() loginRequestDto: LoginRequestDto): Promise<{ accessToken: string }> {
const { username, password } = loginRequestDto;
return await this.adminService.login(username, password);
}
}
93 changes: 93 additions & 0 deletions apps/web-api/src/admin/participants-request.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
Body,
Controller,
Get,
Param,
Patch,
Put,
Query,
UseGuards,
UsePipes,
BadRequestException,
NotFoundException
} from '@nestjs/common';
import { NoCache } from '../decorators/no-cache.decorator';
import { ParticipantsRequestService } from '../participants-request/participants-request.service';
import { AdminAuthGuard } from '../guards/admin-auth.guard';
import { ParticipantsReqValidationPipe } from '../pipes/participant-request-validation.pipe';
import { ProcessParticipantReqDto } from 'libs/contracts/src/schema';
import { ApprovalStatus, ParticipantsRequest, ParticipantType } from '@prisma/client';

@Controller('v1/admin/participants-request')
@UseGuards(AdminAuthGuard)
export class AdminParticipantsRequestController {
constructor(
private readonly participantsRequestService: ParticipantsRequestService
) {}

/**
* Retrieve all participants requests based on query parameters.
* @param query - Filter parameters for participants requests
* @returns A list of participants requests
*/
@Get("/")
@NoCache()
async findAll(@Query() query) {
return this.participantsRequestService.getAll(query);
}

/**
* Retrieve a single participants request by its UID.
* @param uid - The unique identifier of the participants request
* @returns The participants request entry matching the UID
*/
@Get("/:uid")
@NoCache()
async findOne(@Param('uid') uid: string) {
return await this.participantsRequestService.findOneByUid(uid);
}

/**
* Update an existing participants request by its UID.
* @param body - The updated data for the participants request
* @param uid - The unique identifier of the participants request
* @returns The updated participants request entry
*/
@Put('/:uid')
@UsePipes(new ParticipantsReqValidationPipe())
async updateRequest(
@Body() body: any,
@Param('uid') uid: string
) {
return await this.participantsRequestService.updateByUid(uid, body);
}

/**
* Process (approve/reject) a pending participants request.
* @param body - The request body containing the status for processing (e.g., approve/reject)
* @param uid - The unique identifier of the participants request
* @returns The result of processing the participants request
*/
@Patch('/:uid')
async processRequest(
@Param('uid') uid: string,
@Body() body: ProcessParticipantReqDto
): Promise<any> {
const participantRequest: ParticipantsRequest | null = await this.participantsRequestService.findOneByUid(uid);
if (!participantRequest) {
throw new NotFoundException('Request not found');
}
if (participantRequest.status !== ApprovalStatus.PENDING) {
throw new BadRequestException(
`Request cannot be processed. It has already been ${participantRequest.status.toLowerCase()}.`
);
}
if (participantRequest.participantType === ParticipantType.TEAM && !participantRequest.requesterEmailId) {
throw new BadRequestException(
'Requester email is required for team participation requests. Please provide a valid email address.'
);
}
return await this.participantsRequestService.processRequestByUid(uid, participantRequest, body.status);
}
}

Loading

0 comments on commit 5f74020

Please sign in to comment.