diff --git a/src/adapters/postgres/cohort-adapter.ts b/src/adapters/postgres/cohort-adapter.ts index f0718692..e577db01 100644 --- a/src/adapters/postgres/cohort-adapter.ts +++ b/src/adapters/postgres/cohort-adapter.ts @@ -47,21 +47,21 @@ export class PostgresCohortService { public async getCohortsDetails(requiredData, res) { const apiId = APIID.COHORT_READ; - const cohortAcademicYear: any[] = - await this.postgresCohortMembersService.isCohortExistForYear( - requiredData.academicYearId, - requiredData.cohortId - ); - - if (cohortAcademicYear.length !== 1) { - return APIResponse.error( - res, - apiId, - "BAD_REQUEST", - API_RESPONSES.COHORT_NOT_IN_ACADEMIC_YEAR, - HttpStatus.BAD_REQUEST - ); - } + // const cohortAcademicYear: any[] = + // await this.postgresCohortMembersService.isCohortExistForYear( + // requiredData.academicYearId, + // requiredData.cohortId + // ); + + // if (cohortAcademicYear.length !== 1) { + // return APIResponse.error( + // res, + // apiId, + // "BAD_REQUEST", + // API_RESPONSES.COHORT_NOT_IN_ACADEMIC_YEAR, + // HttpStatus.BAD_REQUEST + // ); + // } try { const cohorts = await this.cohortRepository.find({ diff --git a/src/adapters/postgres/cohortAcademicYear-adapter.ts b/src/adapters/postgres/cohortAcademicYear-adapter.ts index 5bdb8897..01e42eff 100644 --- a/src/adapters/postgres/cohortAcademicYear-adapter.ts +++ b/src/adapters/postgres/cohortAcademicYear-adapter.ts @@ -34,7 +34,6 @@ export class CohortAcademicYearService implements IServiceLocatorCohortAcademicY where: { cohortId: cohortAcademicYearDto.cohortId, status: 'active' }, }); - if (!existingCohort) { return APIResponse.error( response, @@ -45,6 +44,16 @@ export class CohortAcademicYearService implements IServiceLocatorCohortAcademicY ); } + if (existingCohort.tenantId !== tenantId) { + return APIResponse.error( + response, + apiId, + HttpStatus.BAD_REQUEST.toLocaleString(), + API_RESPONSES.TENANTID_MISMATCHED, + HttpStatus.BAD_REQUEST + ); + } + // verify if the academic year id is valid const academicYear = await this.postgresAcademicYearService.getActiveAcademicYear( @@ -117,4 +126,11 @@ export class CohortAcademicYearService implements IServiceLocatorCohortAcademicY tenantId, ]); } + + async isCohortExistForYear(yearId, cohortId) { + return await this.cohortAcademicYearRepository.find({ + where: { academicYearId: yearId, cohortId: cohortId }, + }); + } + } diff --git a/src/adapters/postgres/postgres-module.ts b/src/adapters/postgres/postgres-module.ts index 7ad5237b..a27b8a16 100644 --- a/src/adapters/postgres/postgres-module.ts +++ b/src/adapters/postgres/postgres-module.ts @@ -20,6 +20,10 @@ import { RolePrivilegeMapping } from "src/rbac/assign-privilege/entities/assign- import { NotificationRequest } from "@utils/notification.axios"; import { JwtUtil } from "@utils/jwt-token"; import { JwtService } from "@nestjs/jwt"; +import { PostgresAcademicYearService } from "./academicyears-adapter"; +import { CohortAcademicYear } from "src/cohortAcademicYear/entities/cohortAcademicYear.entity"; +import { AcademicYear } from "src/academicyears/entities/academicyears-entity"; +import { CohortAcademicYearService } from "./cohortAcademicYear-adapter"; @Module({ imports: [ @@ -37,6 +41,8 @@ import { JwtService } from "@nestjs/jwt"; UserRoleMapping, Role, RolePrivilegeMapping, + CohortAcademicYear, + AcademicYear ]), ], providers: [ @@ -47,6 +53,8 @@ import { JwtService } from "@nestjs/jwt"; NotificationRequest, JwtUtil, JwtService, + CohortAcademicYearService, + PostgresAcademicYearService ], exports: [ PostgresUserService, @@ -55,6 +63,8 @@ import { JwtService } from "@nestjs/jwt"; NotificationRequest, JwtUtil, JwtService, + CohortAcademicYearService, + PostgresAcademicYearService ], }) export class PostgresModule {} diff --git a/src/adapters/postgres/user-adapter.ts b/src/adapters/postgres/user-adapter.ts index 4de215d9..d5088646 100644 --- a/src/adapters/postgres/user-adapter.ts +++ b/src/adapters/postgres/user-adapter.ts @@ -1,4 +1,4 @@ -import { ConsoleLogger, HttpStatus, Injectable } from "@nestjs/common"; +import { HttpStatus, Injectable } from "@nestjs/common"; import { User } from "../../user/entities/user-entity"; import { FieldValues } from "src/fields/entities/fields-values.entity"; import { InjectRepository } from "@nestjs/typeorm"; @@ -35,6 +35,8 @@ import { ConfigService } from "@nestjs/config"; import { formatTime } from "@utils/formatTimeConversion"; import { API_RESPONSES } from "@utils/response.messages"; import { TokenExpiredError, JsonWebTokenError } from 'jsonwebtoken'; +import { CohortAcademicYearService } from "./cohortAcademicYear-adapter"; +import { PostgresAcademicYearService } from "./academicyears-adapter"; @Injectable() export class PostgresUserService implements IServicelocator { @@ -65,7 +67,9 @@ export class PostgresUserService implements IServicelocator { private readonly postgresRoleService: PostgresRoleService, private readonly notificationRequest: NotificationRequest, private readonly jwtUtil: JwtUtil, - private configService: ConfigService // private cohortMemberService: PostgresCohortMembersService, + private configService: ConfigService, + private postgresAcademicYearService: PostgresAcademicYearService, + private cohortAcademicYearService: CohortAcademicYearService ) { this.jwt_secret = this.configService.get("RBAC_JWT_SECRET"); this.jwt_password_reset_expires_In = this.configService.get("PASSWORD_RESET_JWT_EXPIRES_IN"); @@ -744,15 +748,6 @@ export class PostgresUserService implements IServicelocator { userCreateDto.updatedBy = decoded?.sub; } - // const emailId = decoded?.email; - const keycloakResponse1 = await getKeycloakAdminToken(); - const token1 = keycloakResponse1.data.access_token; - console.log(token1); - const email = await this.usersRepository.findOne({ - where: { userId: userCreateDto.createdBy }, - select: ["email"], - }); - let customFieldError; if (userCreateDto.customFields && userCreateDto.customFields.length > 0) { customFieldError = await this.validateCustomField( @@ -773,9 +768,10 @@ export class PostgresUserService implements IServicelocator { } // check and validate all fields - const validatedRoles = await this.validateRequestBody(userCreateDto); + const validatedRoles = await this.validateRequestBody(userCreateDto, academicYearId); - if (validatedRoles) { + // check if roles are invalid and academic year is provided + if ((!Array.isArray(validatedRoles) || !validatedRoles.every(role => role instanceof Role)) && academicYearId?.length) { return APIResponse.error( response, apiId, @@ -801,9 +797,9 @@ export class PostgresUserService implements IServicelocator { return APIResponse.error( response, apiId, - "Forbidden", + "Bad Request", `User Already Exist`, - HttpStatus.FORBIDDEN + HttpStatus.BAD_REQUEST ); } resKeycloak = await createUserInKeyCloak(userSchema, token).catch( @@ -821,6 +817,8 @@ export class PostgresUserService implements IServicelocator { userCreateDto.userId = resKeycloak; + // if cohort given then check for academic year + const result = await this.createUserInDatabase( request, userCreateDto, @@ -837,7 +835,7 @@ export class PostgresUserService implements IServicelocator { const userId = result?.userId; let roles; - if (validatedRoles) { + if (validatedRoles && academicYearId) { roles = validatedRoles?.map(({ code }) => code?.toUpperCase()); } @@ -901,8 +899,8 @@ export class PostgresUserService implements IServicelocator { } } - async validateRequestBody(userCreateDto) { - const roleData = []; + async validateRequestBody(userCreateDto, academicYearId) { + let roleData: any[] = []; const duplicateTenet = []; const error = []; @@ -938,9 +936,20 @@ export class PostgresUserService implements IServicelocator { } } + if (!academicYearId?.length) { + return false; + } + if (userCreateDto.tenantCohortRoleMapping) { for (const tenantCohortRoleMapping of userCreateDto?.tenantCohortRoleMapping) { - const { tenantId, cohortId, roleId } = tenantCohortRoleMapping; + + const { tenantId, cohortIds, roleId } = tenantCohortRoleMapping; + + // check academic year exists for tenant + const checkAcadmicYear = await this.postgresAcademicYearService.getActiveAcademicYear(academicYearId, tenantId); + if (!checkAcadmicYear) { + error.push("Academic year not found for tenant") + } if (duplicateTenet.includes(tenantId)) { error.push( @@ -958,8 +967,8 @@ export class PostgresUserService implements IServicelocator { tenantId ? this.tenantsRepository.find({ where: { tenantId } }) : Promise.resolve(null), - tenantId && cohortId - ? this.checkCohort(tenantId, cohortId) + tenantId && cohortIds + ? this.checkCohortExistsInAcademicYear(academicYearId, cohortIds) : Promise.resolve(null), roleId ? this.roleRepository.find({ where: { roleId, tenantId } }) @@ -980,6 +989,8 @@ export class PostgresUserService implements IServicelocator { error.push( `Role Id '${roleId}' does not exist for this tenant '${tenantId}'.` ); + } else { + roleData = [...roleData, ...roleExists] } } if (error.length > 0) { @@ -988,16 +999,19 @@ export class PostgresUserService implements IServicelocator { } else { return false; } + return roleData; } - async checkCohort(tenantId: any, cohortData: any) { + async checkCohortExistsInAcademicYear(academicYearId: any, cohortData: any[]) { const notExistCohort = []; for (const cohortId of cohortData) { - const findCohortData = await this.cohortRepository.findOne({ - where: { tenantId, cohortId }, - }); + // const findCohortData = await this.cohortRepository.findOne({ + // where: { tenantId, cohortId }, + // }); - if (!findCohortData) { + const findCohortData = await this.cohortAcademicYearService.isCohortExistForYear(academicYearId, cohortId) + + if (!findCohortData?.length) { notExistCohort.push(cohortId); } } @@ -1026,7 +1040,7 @@ export class PostgresUserService implements IServicelocator { async checkUserinKeyCloakandDb(userDto) { const keycloakResponse = await getKeycloakAdminToken(); const token = keycloakResponse.data.access_token; - console.log(token); + if (userDto?.username) { const usernameExistsInKeycloak = await checkIfUsernameExistsInKeycloak( userDto?.username, @@ -1074,12 +1088,13 @@ export class PostgresUserService implements IServicelocator { if (result && userCreateDto.tenantCohortRoleMapping) { for (const mapData of userCreateDto.tenantCohortRoleMapping) { - if (mapData.cohortId) { - for (const cohortIds of mapData.cohortId) { + if (mapData.cohortIds) { + for (const cohortIds of mapData.cohortIds) { let query = `SELECT * FROM public."CohortAcademicYear" WHERE "cohortId"= '${cohortIds}' AND "academicYearId" = '${academicYearId}'` let getCohortAcademicYearId = await this.usersRepository.query(query); + // will add data only if cohort is found with acadmic year let cohortData = { userId: result?.userId, cohortId: cohortIds, @@ -1330,7 +1345,7 @@ export class PostgresUserService implements IServicelocator { ); if (getFieldDetails == null) { - return (error = "Field not found"); + return ("Field not found"); } if (encounteredKeys.includes(fieldId)) { @@ -1416,7 +1431,7 @@ export class PostgresUserService implements IServicelocator { .map((fieldValue) => fieldValue.fieldId); if (invalidFieldIds.length > 0) { - return (error = `The following fields are not valid for this user: ${invalidFieldIds.join( + return (`The following fields are not valid for this user: ${invalidFieldIds.join( ", " )}.`); } diff --git a/src/cohort/cohort.controller.ts b/src/cohort/cohort.controller.ts index 7e0d6e3c..83684655 100644 --- a/src/cohort/cohort.controller.ts +++ b/src/cohort/cohort.controller.ts @@ -80,12 +80,12 @@ export class CohortController { ) { const tenantId = headers["tenantid"]; const academicYearId = headers["academicyearid"]; - if (!tenantId || !isUUID(tenantId)) { - throw new BadRequestException(API_RESPONSES.TENANTID_VALIDATION); - } - if (!academicYearId || !isUUID(academicYearId)) { - throw new BadRequestException(API_RESPONSES.ACADEMICYEARID_VALIDATION); - } + // if (!tenantId || !isUUID(tenantId)) { + // throw new BadRequestException(API_RESPONSES.TENANTID_VALIDATION); + // } + // if (!academicYearId || !isUUID(academicYearId)) { + // throw new BadRequestException(API_RESPONSES.ACADEMICYEARID_VALIDATION); + // } const getChildDataValueBoolean = children === "true"; const fieldValueBooelan = customField === "true"; const requiredData = { diff --git a/src/cohortMembers/cohortMembers.module.ts b/src/cohortMembers/cohortMembers.module.ts index 747d058f..638f2328 100644 --- a/src/cohortMembers/cohortMembers.module.ts +++ b/src/cohortMembers/cohortMembers.module.ts @@ -32,5 +32,6 @@ import { AcademicYear } from "src/academicyears/entities/academicyears-entity"; PostgresCohortMembersService, PostgresAcademicYearService, ], + exports: [PostgresCohortMembersService] }) export class CohortMembersModule {} diff --git a/src/common/utils/response.messages.ts b/src/common/utils/response.messages.ts index b8b15063..e2256a0f 100644 --- a/src/common/utils/response.messages.ts +++ b/src/common/utils/response.messages.ts @@ -49,6 +49,7 @@ export const API_RESPONSES = { COHORT_NOT_FOUND: 'Cohort not found', FORM_EXISTS: 'Form already exists', INVALID_FORM: 'Invalid form', + TENANTID_MISMATCHED: 'Tenant id mismatched', INVALID_CONTEXT: (context) => `Invalid context: ${context}`, INVALID_CONTEXTTYPE: (context, validContextTypes) => `Invalid contextType. For the context '${context}', it must be one of: ${validContextTypes}`, COHORTID_NOTFOUND_FOT_THIS_YEAR: (cohortId) => `Cohort with cohortId ${cohortId} does not exist for this academic year`, diff --git a/src/user/dto/user-create.dto.ts b/src/user/dto/user-create.dto.ts index 03d9083c..3a077c0c 100644 --- a/src/user/dto/user-create.dto.ts +++ b/src/user/dto/user-create.dto.ts @@ -30,8 +30,9 @@ export class tenantRoleMappingDto { }) @Expose() @IsOptional() + @IsArray() @IsUUID(undefined, { each: true }) - cohortId: string[]; + cohortIds: string[]; @ApiPropertyOptional({ type: String, diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index dcf811a3..6dd7740d 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -123,11 +123,11 @@ export class UserController { @Res() response: Response ) { const academicYearId = headers["academicyearid"]; - if (!academicYearId || !isUUID(academicYearId)) { - throw new BadRequestException( - "academicYearId is required and academicYearId must be a valid UUID." - ); - } + // if (!academicYearId || !isUUID(academicYearId)) { + // throw new BadRequestException( + // "academicYearId is required and academicYearId must be a valid UUID." + // ); + // } return await this.userAdapter .buildUserAdapter() .createUser(request, userCreateDto, academicYearId, response); diff --git a/src/user/user.module.ts b/src/user/user.module.ts index 4d05d8de..7e93e6fc 100644 --- a/src/user/user.module.ts +++ b/src/user/user.module.ts @@ -1,4 +1,4 @@ -import { CacheModule, Module } from "@nestjs/common"; +import { Module } from "@nestjs/common"; import { UserController } from "./user.controller"; import { HttpModule } from "@nestjs/axios"; import { UserAdapter } from "./useradapter"; @@ -13,6 +13,7 @@ import { Tenants } from "src/userTenantMapping/entities/tenant.entity"; import { UserRoleMapping } from "src/rbac/assign-role/entities/assign-role.entity"; import { Cohort } from "src/cohort/entities/cohort.entity"; import { Role } from "src/rbac/role/entities/role.entity"; +import { CohortMembersModule } from "src/cohortMembers/cohortMembers.module"; @Module({ imports: [ @@ -29,6 +30,7 @@ import { Role } from "src/rbac/role/entities/role.entity"; ]), HttpModule, PostgresModule, + CohortMembersModule ], controllers: [UserController], providers: [UserAdapter],