Skip to content

Commit

Permalink
add user, deactivate user func
Browse files Browse the repository at this point in the history
  • Loading branch information
Scarlett-Truong committed Jan 6, 2025
1 parent adeb0a2 commit af937d6
Show file tree
Hide file tree
Showing 27 changed files with 678 additions and 373 deletions.
19 changes: 19 additions & 0 deletions backend/src/external_api/css/css.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ExternalApiService } from "../external-api-service";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { get } from "../../helpers/axios-api";
import { ConfigurationService } from "../../v1/configuration/configuration.service";
import { CssUser } from "src/types/css/cssUser";

@Injectable()
export class CssService implements ExternalApiService {
Expand Down Expand Up @@ -64,6 +65,24 @@ export class CssService implements ExternalApiService {
}
};

getUserIdirByEmail = async (email: string): Promise<CssUser[]> => {
try {
const apiToken = await this.authenticate();
const url = `${this.baseUri}/api/v1/${this.env}/idir/users?email=${email}`;
const config: AxiosRequestConfig = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`,
},
};
const response = await get(apiToken, url, config);
return response?.data.data;
} catch (error) {
this.logger.error(`exception: unable to get user by email: ${email} - error: ${error}`);
throw new Error(`exception: unable to get user by email: ${email} - error: ${error}`);
}
};

getUserRoles = async (userIdir): Promise<{ name: string; composite: string }[]> => {
try {
const apiToken = await this.authenticate();
Expand Down
11 changes: 11 additions & 0 deletions backend/src/types/css/cssUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface CssUser {
username: string;
firstName: string;
lastName: string;
email: string;
attributes: {
idir_user_guid: string[];
idir_username: string[];
display_name: string[];
};
}
26 changes: 26 additions & 0 deletions backend/src/types/models/people/officer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export interface NewOfficer {
user_id: string;
create_user_id: string;
create_utc_timestamp: Date;
update_user_id: string;
update_utc_timestamp: Date;
auth_user_guid: string;
office_guid: string | null;
team_code: string | null;
person_guid: {
first_name: string;
middle_name_1: null;
middle_name_2: null;
last_name: string;
create_user_id: string;
create_utc_timestamp: Date;
update_user_id: string;
updateTimestamp: Date;
};
roles: {
user_roles: Array<{ name: string | undefined }>;
user_idir: string; //Example : fohe4m5pn8clhkxmlho33sn1r7vr7m67@idir
};
coms_enrolled_ind: boolean;
deactivate_ind: boolean;
}
3 changes: 3 additions & 0 deletions backend/src/v1/officer/dto/create-officer.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ export class CreateOfficerDto extends PickType(OfficerDto, [
"user_id",
"person_guid",
"office_guid",
"auth_user_guid",
"create_user_id",
"create_utc_timestamp",
"update_user_id",
"update_utc_timestamp",
"coms_enrolled_ind",
"deactivate_ind",
] as const) {}
12 changes: 12 additions & 0 deletions backend/src/v1/officer/dto/officer.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,16 @@ export class OfficerDto {
description: "The keycloak guid for the officer",
})
auth_user_guid: UUID;

@ApiProperty({
example: "true",
description: "An indicator to determine if the officer has access to COMS",
})
coms_enrolled_ind: boolean;

@ApiProperty({
example: "false",
description: "An indicator to determine if the officer has been deactivated",
})
deactivate_ind: boolean;
}
4 changes: 3 additions & 1 deletion backend/src/v1/officer/dto/update-officer.dto.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { PartialType } from "@nestjs/swagger";
import { CreateOfficerDto } from "./create-officer.dto";

export class UpdateOfficerDto extends PartialType(CreateOfficerDto) {}
export class UpdateOfficerDto extends PartialType(CreateOfficerDto) {
user_roles?: string[];
}
7 changes: 7 additions & 0 deletions backend/src/v1/officer/entities/officer.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,13 @@ export class Officer {
@Column()
coms_enrolled_ind: boolean;

@ApiProperty({
example: false,
description: "Indicates whether an officer has been deactivated",
})
@Column()
deactivate_ind: boolean;

user_roles: string[];
@AfterLoad()
updateUserRoles() {
Expand Down
11 changes: 9 additions & 2 deletions backend/src/v1/officer/officer.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Put } from "@nestjs/common";
import { Controller, Get, Post, Body, Patch, Param, Delete, UseGuards, Put, Inject } from "@nestjs/common";
import { OfficerService } from "./officer.service";
import { CreateOfficerDto } from "./dto/create-officer.dto";
import { UpdateOfficerDto } from "./dto/update-officer.dto";
Expand All @@ -9,6 +9,7 @@ import { ApiTags } from "@nestjs/swagger";
import { UUID } from "crypto";
import { User } from "../../auth/decorators/user.decorator";
import { Token } from "../../auth/decorators/token.decorator";
import { NewOfficer } from "src/types/models/people/officer";

@ApiTags("officer")
@UseGuards(JwtRoleGuard)
Expand All @@ -21,7 +22,7 @@ export class OfficerController {

@Post()
@Roles(Role.COS_OFFICER)
create(@Body() createOfficerDto: CreateOfficerDto) {
create(@Body() createOfficerDto: NewOfficer) {
return this.officerService.create(createOfficerDto);
}

Expand Down Expand Up @@ -61,6 +62,12 @@ export class OfficerController {
return this.officerService.findByPersonGuid(person_guid);
}

@Get("/find-by-email/:email")
@Roles(Role.TEMPORARY_TEST_ADMIN)
findUserByEmail(@Param("email") email: string) {
return this.officerService.findByCssEmail(email);
}

@Patch(":id")
@Roles(Role.COS_OFFICER, Role.CEEB, Role.TEMPORARY_TEST_ADMIN)
update(@Param("id") id: UUID, @Body() updateOfficerDto: UpdateOfficerDto) {
Expand Down
10 changes: 8 additions & 2 deletions backend/src/v1/officer/officer.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,23 @@ import { TypeOrmModule } from "@nestjs/typeorm";
import { Officer } from "./entities/officer.entity";
import { Person } from "../person/entities/person.entity";
import { Office } from "../office/entities/office.entity";
import { CssModule } from "src/external_api/css/css.module";
import { CssModule } from "../../external_api/css/css.module";
import { TeamService } from "src/v1/team/team.service";
import { Team } from "src/v1/team/entities/team.entity";
import { OfficerTeamXref } from "src/v1/officer_team_xref/entities/officer_team_xref.entity";
import { OfficerTeamXrefService } from "src/v1/officer_team_xref/officer_team_xref.service";

@Module({
imports: [
TypeOrmModule.forFeature([Officer]),
TypeOrmModule.forFeature([Person]),
TypeOrmModule.forFeature([Office]),
TypeOrmModule.forFeature([Team]),
TypeOrmModule.forFeature([OfficerTeamXref]),
CssModule,
],
controllers: [OfficerController],
providers: [OfficerService, PersonService, OfficeService],
providers: [OfficerService, PersonService, OfficeService, TeamService, OfficerTeamXrefService],
exports: [OfficerService],
})
export class OfficerModule {}
115 changes: 79 additions & 36 deletions backend/src/v1/officer/officer.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import { CreateOfficerDto } from "./dto/create-officer.dto";
import { CreatePersonDto } from "../person/dto/create-person.dto";
import { UpdateOfficerDto } from "./dto/update-officer.dto";
import { Officer } from "./entities/officer.entity";
import { Office } from "../office/entities/office.entity";
import { AgencyCode } from "../agency_code/entities/agency_code.entity";
import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, Repository } from "typeorm";
import { PersonService } from "../person/person.service";
Expand All @@ -13,18 +11,28 @@ import { UUID } from "crypto";
import { CssService } from "../../external_api/css/css.service";
import { Role } from "../../enum/role.enum";
import { put } from "../../helpers/axios-api";
import { CssUser } from "../../types/css/cssUser";
import { CreateOfficerTeamXrefDto } from "../officer_team_xref/dto/create-officer_team_xref.dto";
import { TeamService } from "../team/team.service";
import { OfficerTeamXrefService } from "../officer_team_xref/officer_team_xref.service";
import { NewOfficer } from "../../types/models/people/officer";

@Injectable()
export class OfficerService {
private readonly logger = new Logger(OfficerService.name);

constructor(private dataSource: DataSource) {}
constructor(private readonly dataSource: DataSource) {}
@InjectRepository(Officer)
private officerRepository: Repository<Officer>;
private readonly officerRepository: Repository<Officer>;

@Inject(PersonService)
protected readonly personService: PersonService;
@Inject(OfficeService)
protected readonly officeService: OfficeService;
@Inject(TeamService)
protected readonly teamService: TeamService;
@Inject(OfficerTeamXrefService)
protected readonly officerTeamXrefService: OfficerTeamXrefService;
@Inject(CssService)
private readonly cssService: CssService;

Expand Down Expand Up @@ -55,6 +63,8 @@ export class OfficerService {
update_user_id: officer.update_user_id,
update_utc_timestamp: officer.update_utc_timestamp,
auth_user_guid: officer.auth_user_guid,
coms_enrolled_ind: officer.coms_enrolled_ind,
deactivate_ind: officer.deactivate_ind,
user_roles: roleMapping[useGuid] ?? [],
} as Officer;
});
Expand Down Expand Up @@ -93,6 +103,20 @@ export class OfficerService {
});
}

async findByCssEmail(email: string): Promise<CssUser | Officer | null> {
const cssUser = await this.cssService.getUserIdirByEmail(email);
if (cssUser.length === 0) return null;
if (cssUser.length > 0) {
//assume email is unique and return only 1 result
const officer = await this.findByAuthUserGuid(cssUser[0].attributes.idir_user_guid[0]);
//if user already exists in officer table, then return officer data
if (officer) {
return officer;
}
}
return cssUser[0];
}

async findByUserId(userid: string): Promise<Officer> {
userid = userid.toUpperCase();
return this.officerRepository.findOne({
Expand All @@ -119,60 +143,79 @@ export class OfficerService {
});
}

async create(officer: any): Promise<Officer> {
async create(officer: NewOfficer): Promise<Officer> {
const queryRunner = this.dataSource.createQueryRunner();

await queryRunner.connect();
await queryRunner.startTransaction();
let newOfficerString;
let officeObject;
let newOfficerObject;
let personObject;
let teamObject;

try {
//Look for the Office
officeObject = await this.officeService.findByGeoOrgCode(officer.geo_organization_unit_code);
if (officeObject.length === 0) {
// insertOffice

let agencyObject = new AgencyCode("COS");
let officeObject = new Office();

officeObject.agency_code = agencyObject;
officeObject.cos_geo_org_unit = officer.geo_organization_unit_code;
officeObject.create_user_id = officer.create_user_id;
officeObject.create_utc_timestamp = officer.create_utc_timestamp;
officeObject.update_user_id = officer.update_user_id;
officeObject.update_utc_timestamp = officer.update_utc_timestamp;

officeObject = await this.officeService.create(officeObject);
officer.office_guid = officeObject.office_guid;
} else {
// use the existing one
officer.office_guid = officeObject[0].office_guid;
}

//Will always insert the person
personObject = await this.personService.createInTransaction(<CreatePersonDto>officer, queryRunner);
personObject = await this.personService.createInTransaction(
<CreatePersonDto>(<unknown>officer.person_guid),
queryRunner,
);
officer.person_guid = personObject.person_guid;

newOfficerString = await this.officerRepository.create(<CreateOfficerDto>officer);
await queryRunner.manager.save(newOfficerString);
newOfficerObject = this.officerRepository.create(<CreateOfficerDto>(<unknown>officer));
await queryRunner.manager.save(newOfficerObject);

//Create team
if (officer.team_code) {
const teamGuid = await this.teamService.findByTeamCodeAndAgencyCode(officer.team_code, "EPO");
const teamEntity = {
officer_guid: newOfficerObject.officer_guid,
team_guid: teamGuid,
active_ind: true,
create_user_id: officer.create_user_id,
update_user_id: officer.update_user_id,
};
teamObject = await this.officerTeamXrefService.createInTransaction(
<CreateOfficerTeamXrefDto>teamEntity,
queryRunner,
);
}
await queryRunner.commitTransaction();

//Create roles
await this.cssService.updateUserRole(officer.roles.user_idir, officer.roles.user_roles);
} catch (err) {
this.logger.error(err);
//rollback all transactions
await queryRunner.rollbackTransaction();
newOfficerString = "Error Occured";
newOfficerObject = null;
//remove all css roles
for await (const roleItem of officer.roles.user_roles) {
await this.cssService.deleteUserRole(officer.roles.user_idir, roleItem.name);
}
} finally {
await queryRunner.release();
}
return newOfficerString;
return newOfficerObject;
}

async update(officer_guid: UUID, updateOfficerDto: UpdateOfficerDto): Promise<Officer> {
const userRoles = updateOfficerDto.user_roles;
//exclude roles field populated from keycloak from update
delete (updateOfficerDto as any).user_roles;
await this.officerRepository.update({ officer_guid }, updateOfficerDto);
return this.findOne(officer_guid);

try {
await this.officerRepository.update({ officer_guid }, updateOfficerDto);

//remove all roles if deactivate_ind is true
if (updateOfficerDto.deactivate_ind === true) {
const officerIdirUsername = `${updateOfficerDto.auth_user_guid.split("-").join("")}@idir`;
for await (const roleItem of userRoles) {
await this.cssService.deleteUserRole(officerIdirUsername, roleItem);
}
}
return this.findOne(officer_guid);
} catch (e) {
this.logger.error(e);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { PickType } from "@nestjs/swagger";
import { OfficerTeamXrefDto } from "./officer_team_xref.dto";

export class CreateOfficerTeamXrefDto extends PickType(OfficerTeamXrefDto, [
"officer_team_xref_guid",
"officer_guid",
"team_guid",
"create_user_id",
Expand Down
Loading

0 comments on commit af937d6

Please sign in to comment.