fix emp temp and add script clear org dna at keycloak
This commit is contained in:
parent
b714dfe239
commit
49a8494a8d
3 changed files with 363 additions and 57 deletions
|
|
@ -187,6 +187,48 @@ export class KeycloakSyncController extends Controller {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear org DNA attributes for profiles (Admin only)
|
||||||
|
*
|
||||||
|
* @summary Clear org DNA attributes in Keycloak for given profiles (ADMIN)
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* This endpoint will:
|
||||||
|
* - Clear all org DNA fields (orgRootDnaId, orgChild1-4DnaId) by setting them to empty strings
|
||||||
|
* - Use when an employee leaves their position (current_holderId becomes null)
|
||||||
|
*
|
||||||
|
* @param {request} request Request body containing profileIds array and profileType
|
||||||
|
*/
|
||||||
|
@Post("clear-org-dna")
|
||||||
|
async clearOrgDna(
|
||||||
|
@Body() request: { profileIds: string[]; profileType: "PROFILE" | "PROFILE_EMPLOYEE" },
|
||||||
|
) {
|
||||||
|
const { profileIds, profileType } = request;
|
||||||
|
|
||||||
|
// Validate profileIds
|
||||||
|
if (!profileIds || profileIds.length === 0) {
|
||||||
|
throw new HttpError(HttpStatus.BAD_REQUEST, "profileIds ต้องไม่ว่างเปล่า");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate profileType
|
||||||
|
if (!["PROFILE", "PROFILE_EMPLOYEE"].includes(profileType)) {
|
||||||
|
throw new HttpError(
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
"profileType ต้องเป็น PROFILE หรือ PROFILE_EMPLOYEE เท่านั้น",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.keycloakAttributeService.clearOrgDnaAttributes(
|
||||||
|
profileIds,
|
||||||
|
profileType,
|
||||||
|
);
|
||||||
|
|
||||||
|
return new HttpSuccess({
|
||||||
|
message: "Clear org DNA attributes เสร็จสิ้น",
|
||||||
|
...result,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch sync all users (Admin only)
|
* Batch sync all users (Admin only)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { PosMaster } from "./../entities/PosMaster";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { KeycloakSyncController } from "./KeycloakSyncController";
|
import { KeycloakSyncController } from "./KeycloakSyncController";
|
||||||
import { EmployeePosMaster } from "./../entities/EmployeePosMaster";
|
import { EmployeePosMaster } from "./../entities/EmployeePosMaster";
|
||||||
|
import { EmployeeTempPosMaster } from "./../entities/EmployeeTempPosMaster";
|
||||||
|
|
||||||
interface OrgUpdatePayload {
|
interface OrgUpdatePayload {
|
||||||
profileId: string;
|
profileId: string;
|
||||||
|
|
@ -26,6 +27,7 @@ interface OrgUpdatePayload {
|
||||||
export class ScriptProfileOrgController extends Controller {
|
export class ScriptProfileOrgController extends Controller {
|
||||||
private posMasterRepo = AppDataSource.getRepository(PosMaster);
|
private posMasterRepo = AppDataSource.getRepository(PosMaster);
|
||||||
private employeePosMasterRepo = AppDataSource.getRepository(EmployeePosMaster);
|
private employeePosMasterRepo = AppDataSource.getRepository(EmployeePosMaster);
|
||||||
|
private employeeTempPosMasterRepo = AppDataSource.getRepository(EmployeeTempPosMaster);
|
||||||
|
|
||||||
// Idempotency flag to prevent concurrent runs
|
// Idempotency flag to prevent concurrent runs
|
||||||
private isRunning = false;
|
private isRunning = false;
|
||||||
|
|
@ -37,6 +39,11 @@ export class ScriptProfileOrgController extends Controller {
|
||||||
10,
|
10,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script to update profile's organizational structure in leave service and sync to Keycloak
|
||||||
|
*
|
||||||
|
* @summary Update org structure for profiles updated within a certain time window and sync to Keycloak
|
||||||
|
*/
|
||||||
@Post("update-org")
|
@Post("update-org")
|
||||||
public async cronjobUpdateOrg(@Request() request: RequestWithUser) {
|
public async cronjobUpdateOrg(@Request() request: RequestWithUser) {
|
||||||
// Idempotency check - prevent concurrent runs
|
// Idempotency check - prevent concurrent runs
|
||||||
|
|
@ -61,7 +68,7 @@ export class ScriptProfileOrgController extends Controller {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Query with optimized select - only fetch required fields
|
// Query with optimized select - only fetch required fields
|
||||||
const [posMasters, posMasterEmployee] = await Promise.all([
|
const [posMasters, posMasterEmployee, posMasterEmployeeTemp] = await Promise.all([
|
||||||
this.posMasterRepo.find({
|
this.posMasterRepo.find({
|
||||||
where: {
|
where: {
|
||||||
lastUpdatedAt: MoreThanOrEqual(windowStart),
|
lastUpdatedAt: MoreThanOrEqual(windowStart),
|
||||||
|
|
@ -120,16 +127,46 @@ export class ScriptProfileOrgController extends Controller {
|
||||||
current_holder: { id: true },
|
current_holder: { id: true },
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
this.employeeTempPosMasterRepo.find({
|
||||||
|
where: {
|
||||||
|
lastUpdatedAt: MoreThanOrEqual(windowStart),
|
||||||
|
orgRevision: {
|
||||||
|
orgRevisionIsCurrent: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
relations: [
|
||||||
|
"orgRevision",
|
||||||
|
"orgRoot",
|
||||||
|
"orgChild1",
|
||||||
|
"orgChild2",
|
||||||
|
"orgChild3",
|
||||||
|
"orgChild4",
|
||||||
|
"current_holder",
|
||||||
|
],
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
current_holderId: true,
|
||||||
|
lastUpdatedAt: true,
|
||||||
|
orgRevision: { id: true },
|
||||||
|
orgRoot: { ancestorDNA: true },
|
||||||
|
orgChild1: { ancestorDNA: true },
|
||||||
|
orgChild2: { ancestorDNA: true },
|
||||||
|
orgChild3: { ancestorDNA: true },
|
||||||
|
orgChild4: { ancestorDNA: true },
|
||||||
|
current_holder: { id: true },
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log("cronjobUpdateOrg: Database query completed", {
|
console.log("cronjobUpdateOrg: Database query completed", {
|
||||||
posMastersCount: posMasters.length,
|
posMastersCount: posMasters.length,
|
||||||
employeePosCount: posMasterEmployee.length,
|
employeePosCount: posMasterEmployee.length,
|
||||||
totalRecords: posMasters.length + posMasterEmployee.length,
|
employeeTempPosCount: posMasterEmployeeTemp.length,
|
||||||
|
totalRecords: posMasters.length + posMasterEmployee.length + posMasterEmployeeTemp.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Build payloads with proper profile type tracking
|
// Build payloads with proper profile type tracking
|
||||||
const payloads = this.buildPayloads(posMasters, posMasterEmployee);
|
const payloads = this.buildPayloads(posMasters, posMasterEmployee, posMasterEmployeeTemp);
|
||||||
|
|
||||||
if (payloads.length === 0) {
|
if (payloads.length === 0) {
|
||||||
console.log("cronjobUpdateOrg: No records to process");
|
console.log("cronjobUpdateOrg: No records to process");
|
||||||
|
|
@ -246,12 +283,13 @@ export class ScriptProfileOrgController extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build payloads from PosMaster and EmployeePosMaster records
|
* Build payloads from PosMaster, EmployeePosMaster, and EmployeeTempPosMaster records
|
||||||
* Includes proper profile type tracking for accurate Keycloak sync
|
* Includes proper profile type tracking for accurate Keycloak sync
|
||||||
*/
|
*/
|
||||||
private buildPayloads(
|
private buildPayloads(
|
||||||
posMasters: PosMaster[],
|
posMasters: PosMaster[],
|
||||||
posMasterEmployee: EmployeePosMaster[],
|
posMasterEmployee: EmployeePosMaster[],
|
||||||
|
posMasterEmployeeTemp: EmployeeTempPosMaster[],
|
||||||
): OrgUpdatePayload[] {
|
): OrgUpdatePayload[] {
|
||||||
const payloads: OrgUpdatePayload[] = [];
|
const payloads: OrgUpdatePayload[] = [];
|
||||||
|
|
||||||
|
|
@ -285,6 +323,21 @@ export class ScriptProfileOrgController extends Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process EmployeeTempPosMaster records (PROFILE_EMPLOYEE type)
|
||||||
|
for (const employeeTempPos of posMasterEmployeeTemp) {
|
||||||
|
if (employeeTempPos.current_holder && employeeTempPos.current_holderId) {
|
||||||
|
payloads.push({
|
||||||
|
profileId: employeeTempPos.current_holderId,
|
||||||
|
rootDnaId: employeeTempPos.orgRoot?.ancestorDNA || null,
|
||||||
|
child1DnaId: employeeTempPos.orgChild1?.ancestorDNA || null,
|
||||||
|
child2DnaId: employeeTempPos.orgChild2?.ancestorDNA || null,
|
||||||
|
child3DnaId: employeeTempPos.orgChild3?.ancestorDNA || null,
|
||||||
|
child4DnaId: employeeTempPos.orgChild4?.ancestorDNA || null,
|
||||||
|
profileType: "PROFILE_EMPLOYEE",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return payloads;
|
return payloads;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,40 +88,101 @@ export class KeycloakAttributeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If not found in Profile, try ProfileEmployee (ลูกจ้าง)
|
// If not found in Profile, try ProfileEmployee (ลูกจ้าง)
|
||||||
const profileEmployeeResult = await this.profileEmployeeRepo
|
// First, get the profileEmployee to check employeeClass
|
||||||
|
const profileEmployeeBasic = await this.profileEmployeeRepo
|
||||||
.createQueryBuilder("pe")
|
.createQueryBuilder("pe")
|
||||||
.leftJoinAndSelect("pe.current_holders", "epm")
|
|
||||||
.leftJoinAndSelect("epm.orgRoot", "org")
|
|
||||||
.leftJoinAndSelect("epm.orgChild1", "orgChild1")
|
|
||||||
.leftJoinAndSelect("epm.orgChild2", "orgChild2")
|
|
||||||
.leftJoinAndSelect("epm.orgChild3", "orgChild3")
|
|
||||||
.leftJoinAndSelect("epm.orgChild4", "orgChild4")
|
|
||||||
.where("pe.keycloak = :keycloakUserId", { keycloakUserId })
|
.where("pe.keycloak = :keycloakUserId", { keycloakUserId })
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (
|
if (!profileEmployeeBasic) {
|
||||||
profileEmployeeResult &&
|
// Return null values if no profile found
|
||||||
profileEmployeeResult.current_holders &&
|
|
||||||
profileEmployeeResult.current_holders.length > 0
|
|
||||||
) {
|
|
||||||
const currentPos = profileEmployeeResult.current_holders[0];
|
|
||||||
const orgRootDnaId = currentPos.orgRoot?.ancestorDNA || "";
|
|
||||||
const orgChild1DnaId = currentPos.orgChild1?.ancestorDNA || "";
|
|
||||||
const orgChild2DnaId = currentPos.orgChild2?.ancestorDNA || "";
|
|
||||||
const orgChild3DnaId = currentPos.orgChild3?.ancestorDNA || "";
|
|
||||||
const orgChild4DnaId = currentPos.orgChild4?.ancestorDNA || "";
|
|
||||||
return {
|
return {
|
||||||
profileId: profileEmployeeResult.id,
|
profileId: null,
|
||||||
orgRootDnaId,
|
orgRootDnaId: null,
|
||||||
orgChild1DnaId,
|
orgChild1DnaId: null,
|
||||||
orgChild2DnaId,
|
orgChild2DnaId: null,
|
||||||
orgChild3DnaId,
|
orgChild3DnaId: null,
|
||||||
orgChild4DnaId,
|
orgChild4DnaId: null,
|
||||||
empType: profileEmployeeResult.employeeClass,
|
empType: null,
|
||||||
prefix: profileEmployeeResult.prefix,
|
prefix: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check employeeClass to determine which table to query
|
||||||
|
const isPermEmployee = profileEmployeeBasic.employeeClass === "PERM";
|
||||||
|
|
||||||
|
if (isPermEmployee) {
|
||||||
|
// ลูกจ้างประจำ (PERM) - ใช้ EmployeePosMaster
|
||||||
|
const profileEmployeeResult = await this.profileEmployeeRepo
|
||||||
|
.createQueryBuilder("pe")
|
||||||
|
.leftJoinAndSelect("pe.current_holders", "epm")
|
||||||
|
.leftJoinAndSelect("epm.orgRoot", "org")
|
||||||
|
.leftJoinAndSelect("epm.orgChild1", "orgChild1")
|
||||||
|
.leftJoinAndSelect("epm.orgChild2", "orgChild2")
|
||||||
|
.leftJoinAndSelect("epm.orgChild3", "orgChild3")
|
||||||
|
.leftJoinAndSelect("epm.orgChild4", "orgChild4")
|
||||||
|
.where("pe.keycloak = :keycloakUserId", { keycloakUserId })
|
||||||
|
.getOne();
|
||||||
|
|
||||||
|
if (
|
||||||
|
profileEmployeeResult &&
|
||||||
|
profileEmployeeResult.current_holders &&
|
||||||
|
profileEmployeeResult.current_holders.length > 0
|
||||||
|
) {
|
||||||
|
const currentPos = profileEmployeeResult.current_holders[0];
|
||||||
|
const orgRootDnaId = currentPos.orgRoot?.ancestorDNA || "";
|
||||||
|
const orgChild1DnaId = currentPos.orgChild1?.ancestorDNA || "";
|
||||||
|
const orgChild2DnaId = currentPos.orgChild2?.ancestorDNA || "";
|
||||||
|
const orgChild3DnaId = currentPos.orgChild3?.ancestorDNA || "";
|
||||||
|
const orgChild4DnaId = currentPos.orgChild4?.ancestorDNA || "";
|
||||||
|
return {
|
||||||
|
profileId: profileEmployeeResult.id,
|
||||||
|
orgRootDnaId,
|
||||||
|
orgChild1DnaId,
|
||||||
|
orgChild2DnaId,
|
||||||
|
orgChild3DnaId,
|
||||||
|
orgChild4DnaId,
|
||||||
|
empType: profileEmployeeResult.employeeClass,
|
||||||
|
prefix: profileEmployeeResult.prefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ลูกจ้างชั่วคราว (TEMP) - ใช้ EmployeeTempPosMaster
|
||||||
|
const profileEmployeeResult = await this.profileEmployeeRepo
|
||||||
|
.createQueryBuilder("pe")
|
||||||
|
.leftJoinAndSelect("pe.current_holderTemps", "etpm")
|
||||||
|
.leftJoinAndSelect("etpm.orgRoot", "org")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild1", "orgChild1")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild2", "orgChild2")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild3", "orgChild3")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild4", "orgChild4")
|
||||||
|
.where("pe.keycloak = :keycloakUserId", { keycloakUserId })
|
||||||
|
.getOne();
|
||||||
|
|
||||||
|
if (
|
||||||
|
profileEmployeeResult &&
|
||||||
|
profileEmployeeResult.current_holderTemps &&
|
||||||
|
profileEmployeeResult.current_holderTemps.length > 0
|
||||||
|
) {
|
||||||
|
const currentPos = profileEmployeeResult.current_holderTemps[0];
|
||||||
|
const orgRootDnaId = currentPos.orgRoot?.ancestorDNA || "";
|
||||||
|
const orgChild1DnaId = currentPos.orgChild1?.ancestorDNA || "";
|
||||||
|
const orgChild2DnaId = currentPos.orgChild2?.ancestorDNA || "";
|
||||||
|
const orgChild3DnaId = currentPos.orgChild3?.ancestorDNA || "";
|
||||||
|
const orgChild4DnaId = currentPos.orgChild4?.ancestorDNA || "";
|
||||||
|
return {
|
||||||
|
profileId: profileEmployeeResult.id,
|
||||||
|
orgRootDnaId,
|
||||||
|
orgChild1DnaId,
|
||||||
|
orgChild2DnaId,
|
||||||
|
orgChild3DnaId,
|
||||||
|
orgChild4DnaId,
|
||||||
|
empType: profileEmployeeResult.employeeClass,
|
||||||
|
prefix: profileEmployeeResult.prefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return null values if no profile found
|
// Return null values if no profile found
|
||||||
return {
|
return {
|
||||||
profileId: null,
|
profileId: null,
|
||||||
|
|
@ -187,40 +248,101 @@ export class KeycloakAttributeService {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const profileEmployeeResult = await this.profileEmployeeRepo
|
// First, get the profileEmployee to check employeeClass
|
||||||
|
const profileEmployeeBasic = await this.profileEmployeeRepo
|
||||||
.createQueryBuilder("pe")
|
.createQueryBuilder("pe")
|
||||||
.leftJoinAndSelect("pe.current_holders", "epm")
|
|
||||||
.leftJoinAndSelect("epm.orgRoot", "org")
|
|
||||||
.leftJoinAndSelect("pm.orgChild1", "orgChild1")
|
|
||||||
.leftJoinAndSelect("pm.orgChild2", "orgChild2")
|
|
||||||
.leftJoinAndSelect("pm.orgChild3", "orgChild3")
|
|
||||||
.leftJoinAndSelect("pm.orgChild4", "orgChild4")
|
|
||||||
.where("pe.id = :profileId", { profileId })
|
.where("pe.id = :profileId", { profileId })
|
||||||
.getOne();
|
.getOne();
|
||||||
|
|
||||||
if (
|
if (!profileEmployeeBasic) {
|
||||||
profileEmployeeResult &&
|
|
||||||
profileEmployeeResult.current_holders &&
|
|
||||||
profileEmployeeResult.current_holders.length > 0
|
|
||||||
) {
|
|
||||||
const currentPos = profileEmployeeResult.current_holders[0];
|
|
||||||
const orgRootDnaId = currentPos.orgRoot?.ancestorDNA || "";
|
|
||||||
const orgChild1DnaId = currentPos.orgChild1?.ancestorDNA || "";
|
|
||||||
const orgChild2DnaId = currentPos.orgChild2?.ancestorDNA || "";
|
|
||||||
const orgChild3DnaId = currentPos.orgChild3?.ancestorDNA || "";
|
|
||||||
const orgChild4DnaId = currentPos.orgChild4?.ancestorDNA || "";
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
profileId: profileEmployeeResult.id,
|
profileId: null,
|
||||||
orgRootDnaId,
|
orgRootDnaId: null,
|
||||||
orgChild1DnaId,
|
orgChild1DnaId: null,
|
||||||
orgChild2DnaId,
|
orgChild2DnaId: null,
|
||||||
orgChild3DnaId,
|
orgChild3DnaId: null,
|
||||||
orgChild4DnaId,
|
orgChild4DnaId: null,
|
||||||
empType: profileEmployeeResult.employeeClass,
|
empType: null,
|
||||||
prefix: profileEmployeeResult.prefix,
|
prefix: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check employeeClass to determine which table to query
|
||||||
|
const isPermEmployee = profileEmployeeBasic.employeeClass === "PERM";
|
||||||
|
|
||||||
|
if (isPermEmployee) {
|
||||||
|
// ลูกจ้างประจำ (PERM) - ใช้ EmployeePosMaster
|
||||||
|
const profileEmployeeResult = await this.profileEmployeeRepo
|
||||||
|
.createQueryBuilder("pe")
|
||||||
|
.leftJoinAndSelect("pe.current_holders", "epm")
|
||||||
|
.leftJoinAndSelect("epm.orgRoot", "org")
|
||||||
|
.leftJoinAndSelect("epm.orgChild1", "orgChild1")
|
||||||
|
.leftJoinAndSelect("epm.orgChild2", "orgChild2")
|
||||||
|
.leftJoinAndSelect("epm.orgChild3", "orgChild3")
|
||||||
|
.leftJoinAndSelect("epm.orgChild4", "orgChild4")
|
||||||
|
.where("pe.id = :profileId", { profileId })
|
||||||
|
.getOne();
|
||||||
|
|
||||||
|
if (
|
||||||
|
profileEmployeeResult &&
|
||||||
|
profileEmployeeResult.current_holders &&
|
||||||
|
profileEmployeeResult.current_holders.length > 0
|
||||||
|
) {
|
||||||
|
const currentPos = profileEmployeeResult.current_holders[0];
|
||||||
|
const orgRootDnaId = currentPos.orgRoot?.ancestorDNA || "";
|
||||||
|
const orgChild1DnaId = currentPos.orgChild1?.ancestorDNA || "";
|
||||||
|
const orgChild2DnaId = currentPos.orgChild2?.ancestorDNA || "";
|
||||||
|
const orgChild3DnaId = currentPos.orgChild3?.ancestorDNA || "";
|
||||||
|
const orgChild4DnaId = currentPos.orgChild4?.ancestorDNA || "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
profileId: profileEmployeeResult.id,
|
||||||
|
orgRootDnaId,
|
||||||
|
orgChild1DnaId,
|
||||||
|
orgChild2DnaId,
|
||||||
|
orgChild3DnaId,
|
||||||
|
orgChild4DnaId,
|
||||||
|
empType: profileEmployeeResult.employeeClass,
|
||||||
|
prefix: profileEmployeeResult.prefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ลูกจ้างชั่วคราว (TEMP) - ใช้ EmployeeTempPosMaster
|
||||||
|
const profileEmployeeResult = await this.profileEmployeeRepo
|
||||||
|
.createQueryBuilder("pe")
|
||||||
|
.leftJoinAndSelect("pe.current_holderTemps", "etpm")
|
||||||
|
.leftJoinAndSelect("etpm.orgRoot", "org")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild1", "orgChild1")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild2", "orgChild2")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild3", "orgChild3")
|
||||||
|
.leftJoinAndSelect("etpm.orgChild4", "orgChild4")
|
||||||
|
.where("pe.id = :profileId", { profileId })
|
||||||
|
.getOne();
|
||||||
|
|
||||||
|
if (
|
||||||
|
profileEmployeeResult &&
|
||||||
|
profileEmployeeResult.current_holderTemps &&
|
||||||
|
profileEmployeeResult.current_holderTemps.length > 0
|
||||||
|
) {
|
||||||
|
const currentPos = profileEmployeeResult.current_holderTemps[0];
|
||||||
|
const orgRootDnaId = currentPos.orgRoot?.ancestorDNA || "";
|
||||||
|
const orgChild1DnaId = currentPos.orgChild1?.ancestorDNA || "";
|
||||||
|
const orgChild2DnaId = currentPos.orgChild2?.ancestorDNA || "";
|
||||||
|
const orgChild3DnaId = currentPos.orgChild3?.ancestorDNA || "";
|
||||||
|
const orgChild4DnaId = currentPos.orgChild4?.ancestorDNA || "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
profileId: profileEmployeeResult.id,
|
||||||
|
orgRootDnaId,
|
||||||
|
orgChild1DnaId,
|
||||||
|
orgChild2DnaId,
|
||||||
|
orgChild3DnaId,
|
||||||
|
orgChild4DnaId,
|
||||||
|
empType: profileEmployeeResult.employeeClass,
|
||||||
|
prefix: profileEmployeeResult.prefix,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -313,6 +435,95 @@ export class KeycloakAttributeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear org DNA attributes in Keycloak for given profiles
|
||||||
|
* Sets all org DNA fields to empty strings
|
||||||
|
*
|
||||||
|
* @param profileIds - Array of profile IDs to clear
|
||||||
|
* @param profileType - 'PROFILE' for officers or 'PROFILE_EMPLOYEE' for employees
|
||||||
|
* @returns Object with success/failed counts and details
|
||||||
|
*/
|
||||||
|
async clearOrgDnaAttributes(
|
||||||
|
profileIds: string[],
|
||||||
|
profileType: "PROFILE" | "PROFILE_EMPLOYEE",
|
||||||
|
): Promise<{
|
||||||
|
total: number;
|
||||||
|
success: number;
|
||||||
|
failed: number;
|
||||||
|
details: Array<{ profileId: string; status: "success" | "failed"; error?: string }>;
|
||||||
|
}> {
|
||||||
|
const result = {
|
||||||
|
total: profileIds.length,
|
||||||
|
success: 0,
|
||||||
|
failed: 0,
|
||||||
|
details: [] as Array<{ profileId: string; status: "success" | "failed"; error?: string }>,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const profileId of profileIds) {
|
||||||
|
try {
|
||||||
|
// Get the keycloak userId from the profile
|
||||||
|
let keycloakUserId: string | null = null;
|
||||||
|
|
||||||
|
if (profileType === "PROFILE") {
|
||||||
|
const profile = await this.profileRepo.findOne({ where: { id: profileId } });
|
||||||
|
keycloakUserId = profile?.keycloak || "";
|
||||||
|
} else {
|
||||||
|
const profileEmployee = await this.profileEmployeeRepo.findOne({
|
||||||
|
where: { id: profileId },
|
||||||
|
});
|
||||||
|
keycloakUserId = profileEmployee?.keycloak || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keycloakUserId) {
|
||||||
|
result.failed++;
|
||||||
|
result.details.push({
|
||||||
|
profileId,
|
||||||
|
status: "failed",
|
||||||
|
error: "No Keycloak user ID found",
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear org DNA attributes by setting them to empty strings
|
||||||
|
const clearedAttributes: Record<string, string[]> = {
|
||||||
|
orgRootDnaId: [""],
|
||||||
|
orgChild1DnaId: [""],
|
||||||
|
orgChild2DnaId: [""],
|
||||||
|
orgChild3DnaId: [""],
|
||||||
|
orgChild4DnaId: [""],
|
||||||
|
};
|
||||||
|
|
||||||
|
const success = await updateUserAttributes(keycloakUserId, clearedAttributes);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
result.success++;
|
||||||
|
result.details.push({
|
||||||
|
profileId,
|
||||||
|
status: "success",
|
||||||
|
});
|
||||||
|
console.log(`Cleared org DNA attributes for profile ${profileId} (${profileType})`);
|
||||||
|
} else {
|
||||||
|
result.failed++;
|
||||||
|
result.details.push({
|
||||||
|
profileId,
|
||||||
|
status: "failed",
|
||||||
|
error: "Failed to update Keycloak attributes",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
result.failed++;
|
||||||
|
result.details.push({
|
||||||
|
profileId,
|
||||||
|
status: "failed",
|
||||||
|
error: error.message || "Unknown error",
|
||||||
|
});
|
||||||
|
console.error(`Error clearing org DNA attributes for profile ${profileId}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Batch sync multiple users with unlimited count and parallel processing
|
* Batch sync multiple users with unlimited count and parallel processing
|
||||||
* Useful for initial sync or periodic updates
|
* Useful for initial sync or periodic updates
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue