diff --git a/src/main.ts b/src/main.ts index 31bc0e1..72843e7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -57,7 +57,7 @@ async function bootstrap() { }) .filter(Boolean); - throw new BadRequestException(newResult[0].message); + throw new BadRequestException(`Bad Request${newResult[0].message !== 'Bad Request' ? " : "+newResult[0].message : ""}`); }; app.useGlobalPipes( diff --git a/src/oracle/oracle.module.ts b/src/oracle/oracle.module.ts index 1a2bfc6..15045b6 100644 --- a/src/oracle/oracle.module.ts +++ b/src/oracle/oracle.module.ts @@ -6,9 +6,11 @@ import { UscibManagedSpModule } from './uscib-managed-sp/uscib-managed-sp.module import { ParamTableModule } from './param-table/param-table.module'; import { ManageFeeModule } from './manage-fee/manage-fee.module'; import { ManageClientsModule } from './manage-clients/manage-clients.module'; +import { UserMaintenanceModule } from './user-maintenance/user-maintenance.module'; @Module({ imports: [ + UserMaintenanceModule, DbModule, HomePageModule, UscibManagedSpModule, diff --git a/src/oracle/user-maintenance/user-maintenance.controller.ts b/src/oracle/user-maintenance/user-maintenance.controller.ts new file mode 100644 index 0000000..9c72af3 --- /dev/null +++ b/src/oracle/user-maintenance/user-maintenance.controller.ts @@ -0,0 +1,41 @@ +import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common'; +import { UserMaintenanceService } from './user-maintenance.service'; +import { ApiTags } from '@nestjs/swagger'; +import { CreateUSCIBLoginsDTO, GetSPUserDetailsDTO, UseSPIDDTO } from './user-maintenance.dto'; + +@ApiTags('User Maintenance - Oracle') +@Controller('oracle') +export class UserMaintenanceController { + constructor(private readonly userMaintenanceService: UserMaintenanceService) { } + + + @Get('GetSPUserDetails/:p_userid') + async GetPreparers(@Param() params: GetSPUserDetailsDTO) { + return await this.userMaintenanceService.GetSPUserDetails(params); + } + + // @ApiTags('User Maintenance - Oracle') + // @Get('GetSPUserDetailsv1') + // async GetPreparersv1(@Query() query: GetSPUserDetailsDTO) { + // return await this.userMaintenanceService.GetSPUserDetailsv1(query); + // } + + // @ApiTags('User Maintenance - Oracle') + @Post('CreateUSCIBLogins') + async CreateUSCIBLogins(@Body() body: CreateUSCIBLoginsDTO) { + return await this.userMaintenanceService.CreateUSCIBLogins(body); + } + + // @ApiTags('User Maintenance - Oracle') + + @Get('GetUSCIBLogins') + async GetUSCIBLogins() { + return await this.userMaintenanceService.GetUSCIBLogins(); + } + + @Get('GetSPLogins/:p_spid') + async GetSPLogins(@Param() params: UseSPIDDTO) { + return await this.userMaintenanceService.GetSPLogins(params); + } + +} diff --git a/src/oracle/user-maintenance/user-maintenance.dto.ts b/src/oracle/user-maintenance/user-maintenance.dto.ts new file mode 100644 index 0000000..54531c3 --- /dev/null +++ b/src/oracle/user-maintenance/user-maintenance.dto.ts @@ -0,0 +1,57 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Transform, Type } from "class-transformer"; +import { IsDefined, IsEmail, IsEnum, IsInt, IsPositive, IsString, Length, Min } from "class-validator"; + +export enum YON { + YES = "Y", + NO = "N", +} + +export class GetSPUserDetailsDTO { + + @ApiProperty({ required: true }) + // @IsEmail({},{message:"Invalid P_USERID property"}) + @IsString() + @IsDefined({ message: "Invalid Request" }) + p_userid: string; +} + +export class CreateUSCIBLoginsDTO { + @ApiProperty({ required: true }) + @IsString() + @IsDefined({ message: "Invalid Request" }) + p_userid: string; + + @ApiProperty({ required: true }) + @IsEmail({}, { message: "Invalid p_emailaddr property" }) + @IsString() + @IsDefined({ message: "Invalid Request" }) + p_emailaddr: string; + + @ApiProperty({ required: true }) + @IsString() + @IsDefined({ message: "Invalid Request" }) + p_lookupCode: string; + + @ApiProperty({ required: true }) + @IsString() + @IsDefined({ message: "Invalid Request" }) + p_password: string; + + @ApiProperty({ required: true, enum: YON }) + @Transform(({ value }) => typeof value === 'string' ? value.trim().toUpperCase() : value) + @IsEnum(YON, { message: 'P_EnablePasswordPolicy must be either "Y" or "N"' }) + @Length(1, 1, { message: 'P_EnablePasswordPolicy must be between 5 and 6 characters' }) + @IsString() + @IsDefined({ message: "Invalid Request" }) + P_EnablePasswordPolicy: YON; +} + +export class UseSPIDDTO { + @ApiProperty({ required: true }) + @Min(0, { message: 'p_spid must be greater than or equal to 0' }) + @IsInt({ message: "p_spid must be whole number" }) + @IsDefined({ message: 'Invalid Request' }) + @Type(() => Number) + p_spid: number; +} \ No newline at end of file diff --git a/src/oracle/user-maintenance/user-maintenance.module.ts b/src/oracle/user-maintenance/user-maintenance.module.ts new file mode 100644 index 0000000..0e707d6 --- /dev/null +++ b/src/oracle/user-maintenance/user-maintenance.module.ts @@ -0,0 +1,9 @@ +import { Module } from '@nestjs/common'; +import { UserMaintenanceController } from './user-maintenance.controller'; +import { UserMaintenanceService } from './user-maintenance.service'; + +@Module({ + controllers: [UserMaintenanceController], + providers: [UserMaintenanceService] +}) +export class UserMaintenanceModule {} diff --git a/src/oracle/user-maintenance/user-maintenance.service.ts b/src/oracle/user-maintenance/user-maintenance.service.ts new file mode 100644 index 0000000..57f8412 --- /dev/null +++ b/src/oracle/user-maintenance/user-maintenance.service.ts @@ -0,0 +1,316 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { OracleDBService } from 'src/db/db.service'; +import { CreateUSCIBLoginsDTO, GetSPUserDetailsDTO, UseSPIDDTO } from './user-maintenance.dto'; +import { InternalServerException } from 'src/exceptions/internalServerError.exception'; +import * as oracledb from 'oracledb'; +import { BadRequestException } from 'src/exceptions/badRequest.exception'; + + +interface RoleDetail { [key: string]: any; } +interface MenuDetail { [key: string]: any; } +interface MenuPageDetail { [key: string]: any; } +interface UserDetail { [key: string]: any; } + +export interface GetUserDetailsResult { + roleDetails: RoleDetail[]; + menuDetails: MenuDetail[]; + menuPageDetails: MenuPageDetail[]; + userDetails: UserDetail[]; +} + +@Injectable() +export class UserMaintenanceService { + + private readonly logger = new Logger(UserMaintenanceService.name); + + constructor(private readonly oracleDBService: OracleDBService) { } + + private async fetchCursor(cursor: oracledb.ResultSet): Promise { + try { + const rows = await cursor.getRows(); // or getRows(1000) if needed + await cursor.close(); + return rows; + } catch (err) { + this.logger.error('Failed to fetch from cursor', err.stack || err); + throw new InternalServerException("Error reading data from database."); + } + } + + async GetSPUserDetails(body: GetSPUserDetailsDTO): Promise { + let connection; + + try { + connection = await this.oracleDBService.getConnection(); + + const result = await connection.execute( + `BEGIN + SPCLIENTUSER_PKG.GetSPUserDetails( + :P_USERID, :P_ROLECUR, :P_MENUCUR, :P_MENUPAGECUR, :P_USERDETAILS + ); + END;`, + { + P_USERID: { val: body.p_userid, type: oracledb.DB_TYPE_NVARCHAR }, + P_ROLECUR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + P_MENUCUR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + P_MENUPAGECUR: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + P_USERDETAILS: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + const outBinds = result.outBinds; + if (!outBinds?.P_ROLECUR || !outBinds?.P_MENUCUR || !outBinds?.P_MENUPAGECUR || !outBinds?.P_USERDETAILS) { + this.logger.error('One or more expected cursors are missing from stored procedure output.'); + throw new InternalServerException("Incomplete data received from the database."); + } + let userdetailsTemp: any = await this.fetchCursor(outBinds.P_USERDETAILS); + return { + roleDetails: await this.fetchCursor(outBinds.P_ROLECUR), + menuDetails: await this.fetchCursor(outBinds.P_MENUCUR), + menuPageDetails: await this.fetchCursor(outBinds.P_MENUPAGECUR), + userDetails: userdetailsTemp[0] + }; + } catch (error) { + if (error instanceof BadRequestException) { + this.logger.warn(error.message); + throw error; + } else if (error.message.includes("NJS-107")) { + this.logger.warn("Invalid cursor encountered: " + error.message); + throw new BadRequestException("Invalid database response."); + } else if (error.message.includes("ORA-")) { + this.logger.error("Oracle error occurred: " + error.message); + throw new InternalServerException(error.message); + } + this.logger.error('GetSPUserDetails failed', error.stack || error); + throw new InternalServerException(error.message); + } finally { + if (connection) { + try { + await connection.close(); + } catch (closeErr) { + this.logger.error('Failed to close DB connection', closeErr); + } + } + } + } + + async GetSPUserDetailsv1(body: GetSPUserDetailsDTO): Promise { + let connection; + + try { + connection = await this.oracleDBService.getConnection(); + + const result = await connection.execute( + `BEGIN + SPCLIENTUSER_PKG.GetSPUserDetails( + :p_userid , :p_rolecur , :p_menucur , :p_menuPageCur , :p_userdetails + ); + END;`, + { + p_userid: { val: body.p_userid, type: oracledb.DB_TYPE_NVARCHAR }, + p_rolecur: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + p_menucur: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + p_menuPageCur: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + p_userdetails: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + const outBinds = result.outBinds; + if (!outBinds?.p_rolecur || !outBinds?.p_menucur || !outBinds?.p_menuPageCur || !outBinds?.p_userdetails) { + this.logger.error('One or more expected cursors are missing from stored procedure output.'); + throw new InternalServerException("Incomplete data received from the database."); + } + + return { + roleDetails: await this.fetchCursor(outBinds.p_rolecur), + menuDetails: await this.fetchCursor(outBinds.p_menucur), + menuPageDetails: await this.fetchCursor(outBinds.p_menuPageCur), + userDetails: await this.fetchCursor(outBinds.p_userdetails), + }; + } catch (error) { + if (error instanceof BadRequestException) { + this.logger.warn(error.message); + throw error; + } else if (error.message.includes("NJS-107")) { + this.logger.warn("Invalid cursor encountered: " + error.message); + throw new BadRequestException("Invalid database response."); + } else if (error.message.includes("ORA-")) { + this.logger.error("Oracle error occurred: " + error.message); + throw new InternalServerException(error.message); + } + this.logger.error('GetSPUserDetails failed', error.stack || error); + throw new InternalServerException(error.message); + } finally { + if (connection) { + try { + await connection.close(); + } catch (closeErr) { + this.logger.error('Failed to close DB connection', closeErr); + } + } + } + } + + async CreateUSCIBLogins(body: CreateUSCIBLoginsDTO) { + + let connection; + + try { + connection = await this.oracleDBService.getConnection(); + + const result = await connection.execute( + `BEGIN + Userlogin_pkg.CreateUSCIBLogins( + :p_userid , :p_emailaddr , :p_lookupCode , :p_password , :P_EnablePasswordPolicy, :p_cursor + ); + END;`, + { + p_userid: { val: body.p_userid, type: oracledb.DB_TYPE_NVARCHAR }, + p_emailaddr: { val: body.p_emailaddr, type: oracledb.DB_TYPE_NVARCHAR }, + p_lookupCode: { val: body.p_lookupCode, type: oracledb.DB_TYPE_NVARCHAR }, + p_password: { val: body.p_password, type: oracledb.DB_TYPE_NVARCHAR }, + P_EnablePasswordPolicy: { val: body.P_EnablePasswordPolicy, type: oracledb.DB_TYPE_NVARCHAR }, + p_cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + const outBinds = result.outBinds; + if (!outBinds?.p_cursor) { + this.logger.error('One or more expected cursors are missing from stored procedure output.'); + throw new InternalServerException("Incomplete data received from the database."); + } + + return { + data: await this.fetchCursor(outBinds.p_cursor), + }; + } catch (error) { + if (error instanceof BadRequestException) { + this.logger.warn(error.message); + throw error; + } else if (error.message.includes("NJS-107")) { + this.logger.warn("Invalid cursor encountered: " + error.message); + throw new BadRequestException("Invalid database response."); + } else if (error.message.includes("ORA-")) { + this.logger.error("Oracle error occurred: " + error.message); + throw new InternalServerException(error.message); + } + this.logger.error('GetSPUserDetails failed', error.stack || error); + throw new InternalServerException(error.message); + } finally { + if (connection) { + try { + await connection.close(); + } catch (closeErr) { + this.logger.error('Failed to close DB connection', closeErr); + } + } + } + + } + + async GetUSCIBLogins() { + + let connection; + + try { + connection = await this.oracleDBService.getConnection(); + + const result = await connection.execute( + `BEGIN + Userlogin_pkg.GetUSCIBLogins( + :p_cursor + ); + END;`, + { + p_cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + const outBinds = result.outBinds; + if (!outBinds?.p_cursor) { + this.logger.error('One or more expected cursors are missing from stored procedure output.'); + throw new InternalServerException("Incomplete data received from the database."); + } + + return await this.fetchCursor(outBinds.p_cursor); + } catch (error) { + if (error instanceof BadRequestException) { + this.logger.warn(error.message); + throw error; + } else if (error.message.includes("NJS-107")) { + this.logger.warn("Invalid cursor encountered: " + error.message); + throw new BadRequestException("Invalid database response."); + } else if (error.message.includes("ORA-")) { + this.logger.error("Oracle error occurred: " + error.message); + throw new InternalServerException(error.message); + } + this.logger.error('GetSPUserDetails failed', error.stack || error); + throw new InternalServerException(error.message); + } finally { + if (connection) { + try { + await connection.close(); + } catch (closeErr) { + this.logger.error('Failed to close DB connection', closeErr); + } + } + } + + } + + async GetSPLogins(body: UseSPIDDTO) { + + let connection; + + try { + connection = await this.oracleDBService.getConnection(); + + const result = await connection.execute( + `BEGIN + Userlogin_pkg.GetSPLogins( + :p_spid , :p_cursor + ); + END;`, + { + p_spid: { val: body.p_spid, type: oracledb.DB_TYPE_NUMBER }, + p_cursor: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + const outBinds = result.outBinds; + if (!outBinds?.p_cursor) { + this.logger.error('One or more expected cursors are missing from stored procedure output.'); + throw new InternalServerException("Incomplete data received from the database."); + } + + return await this.fetchCursor(outBinds.p_cursor); + } catch (error) { + if (error instanceof BadRequestException) { + this.logger.warn(error.message); + throw error; + } else if (error.message.includes("NJS-107")) { + this.logger.warn("Invalid cursor encountered: " + error.message); + throw new BadRequestException("Invalid database response."); + } else if (error.message.includes("ORA-")) { + this.logger.error("Oracle error occurred: " + error.message); + throw new InternalServerException(error.message); + } + this.logger.error('GetSPUserDetails failed', error.stack || error); + throw new InternalServerException(error.message); + } finally { + if (connection) { + try { + await connection.close(); + } catch (closeErr) { + this.logger.error('Failed to close DB connection', closeErr); + } + } + } + + } + +}