510 lines
19 KiB
TypeScript
510 lines
19 KiB
TypeScript
import { v4 as uuidv4 } from 'uuid';
|
|
|
|
import User from 'src/sdk/User';
|
|
import MessageBus from 'src/sdk/MessageBus';
|
|
|
|
import DatabaseService from './DatabaseService';
|
|
import RuleService from './RuleService';
|
|
import RuleGroupService from './RuleGroupService';
|
|
import RoleService from './RoleService';
|
|
import OfficeRoleService from './OfficeRoleService';
|
|
import { DEFAULT_VALIDATOR_ID } from '@Front/Config/AppConstants';
|
|
|
|
const mandatoryRoles = ['Notaire', 'Collaborateur'];
|
|
|
|
/**
|
|
* Type pour le callback de progression
|
|
*/
|
|
export interface ProgressInfo {
|
|
/** Progression globale (0-100) */
|
|
globalProgress: number;
|
|
/** Nom de l'étape en cours */
|
|
currentStep: string;
|
|
/** Progression de l'étape en cours (0-100) */
|
|
stepProgress: number;
|
|
/** Description optionnelle de l'action en cours */
|
|
description?: string;
|
|
}
|
|
|
|
export default class ImportData {
|
|
|
|
protected static readonly messageBus: MessageBus = MessageBus.getInstance();
|
|
|
|
public static async import(office: any, validatorId: string, onProgress?: (info: ProgressInfo) => void): Promise<void> {
|
|
// Définir les étapes d'importation dynamiquement
|
|
const importSteps = [
|
|
{
|
|
name: 'Règles',
|
|
function: async (progressCallback?: (subProgress: number, description?: string) => void) =>
|
|
await this.importRules(progressCallback)
|
|
},
|
|
{
|
|
name: 'Groupes de règles',
|
|
function: async (progressCallback?: (subProgress: number, description?: string) => void) =>
|
|
await this.importRuleGroups(progressCallback)
|
|
},
|
|
{
|
|
name: 'Rôles',
|
|
function: async (progressCallback?: (subProgress: number, description?: string) => void) =>
|
|
await this.importRoles(progressCallback)
|
|
},
|
|
{
|
|
name: 'Rôles d\'office',
|
|
function: async (progressCallback?: (subProgress: number, description?: string) => void, prevResults?: any[]) =>
|
|
await this.importOfficeRoles(office, prevResults![1], progressCallback)
|
|
}
|
|
];
|
|
|
|
// Calculer la part de progression pour chaque étape
|
|
const totalSteps = importSteps.length;
|
|
const stepWeight = 100 / totalSteps;
|
|
|
|
// Appel du callback avec 0% au début
|
|
onProgress?.({
|
|
globalProgress: 0,
|
|
currentStep: 'Initialisation',
|
|
stepProgress: 0,
|
|
description: 'Début de l\'importation des données'
|
|
});
|
|
|
|
// Exécuter chaque étape d'importation séquentiellement
|
|
const results: any[] = [];
|
|
|
|
for (let i = 0; i < importSteps.length; i++) {
|
|
const step = importSteps[i];
|
|
if (!step) continue; // S'assurer que l'étape existe
|
|
|
|
const startProgress = i * stepWeight;
|
|
|
|
// Créer un callback de progression pour cette étape
|
|
const stepProgressCallback = (subProgress: number, description?: string) => {
|
|
onProgress?.({
|
|
globalProgress: startProgress + (subProgress * stepWeight / 100),
|
|
currentStep: step.name,
|
|
stepProgress: subProgress,
|
|
description
|
|
});
|
|
};
|
|
|
|
// Exécuter l'étape et stocker le résultat si nécessaire
|
|
const result = await step.function(stepProgressCallback, results);
|
|
if (result !== undefined) {
|
|
results.push(result);
|
|
} else {
|
|
// Push empty array to maintain consistent indexing
|
|
results.push([]);
|
|
}
|
|
console.log(`Step ${i} (${step.name}): ${result?.length || 0} items`);
|
|
}
|
|
|
|
if (!await this.isDone()) {
|
|
await this.done(validatorId);
|
|
}
|
|
}
|
|
|
|
public static async isDone(): Promise<boolean> {
|
|
return await this.messageBus.getProcessesDecoded((publicValues: any) =>
|
|
publicValues['uid'] &&
|
|
publicValues['utype'] &&
|
|
publicValues['utype'] === 'importData' &&
|
|
publicValues['isDeleted'] && publicValues['isDeleted'] === 'false'
|
|
).then(async (processes: any[]) => processes.length > 0);
|
|
}
|
|
|
|
private static async done(validatorId: string): Promise<any> {
|
|
const ownerId: string = User.getInstance().getPairingId()!;
|
|
|
|
const processData: any = {
|
|
uid: uuidv4(),
|
|
utype: 'importData',
|
|
isDeleted: 'false',
|
|
created_at: new Date().toISOString(),
|
|
updated_at: new Date().toISOString()
|
|
};
|
|
|
|
const privateFields: string[] = Object.keys(processData);
|
|
privateFields.splice(privateFields.indexOf('uid'), 1);
|
|
privateFields.splice(privateFields.indexOf('utype'), 1);
|
|
privateFields.splice(privateFields.indexOf('isDeleted'), 1);
|
|
|
|
const roles: any = {
|
|
demiurge: {
|
|
members: [ownerId],
|
|
validation_rules: [],
|
|
storages: []
|
|
},
|
|
owner: {
|
|
members: [ownerId],
|
|
validation_rules: [
|
|
{
|
|
quorum: 0.5,
|
|
fields: [...privateFields, 'roles', 'uid', 'utype'],
|
|
min_sig_member: 1,
|
|
},
|
|
],
|
|
storages: []
|
|
},
|
|
validator: {
|
|
members: [validatorId],
|
|
validation_rules: [
|
|
{
|
|
quorum: 0.5,
|
|
fields: ['idCertified', 'roles'],
|
|
min_sig_member: 1,
|
|
},
|
|
{
|
|
quorum: 0.0,
|
|
fields: [...privateFields],
|
|
min_sig_member: 0,
|
|
},
|
|
],
|
|
storages: []
|
|
},
|
|
apophis: {
|
|
members: [ownerId],
|
|
validation_rules: [],
|
|
storages: []
|
|
}
|
|
};
|
|
|
|
return new Promise<void>((resolve: () => void, reject: (error: string) => void) => {
|
|
this.messageBus.createProcess(processData, privateFields, roles).then((processCreated: any) => {
|
|
this.messageBus.notifyUpdate(processCreated.processId, processCreated.process.states[0].state_id).then(() => {
|
|
this.messageBus.validateState(processCreated.processId, processCreated.process.states[0].state_id).then(() => {
|
|
resolve();
|
|
}).catch(reject);
|
|
}).catch(reject);
|
|
}).catch(reject);
|
|
});
|
|
}
|
|
|
|
private static async importRules(onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
|
|
console.log('Importing rules');
|
|
const rules: any[] = [];
|
|
|
|
const INIT_PROGRESS = 0;
|
|
const FETCH_PROGRESS = 30;
|
|
const CREATE_END_PROGRESS = 90;
|
|
const FINAL_PROGRESS = 100;
|
|
onProgress?.(INIT_PROGRESS, 'Initialisation');
|
|
|
|
let page = 1;
|
|
let limit = 10;
|
|
let totalPages = 1;
|
|
|
|
onProgress?.(FETCH_PROGRESS, 'Récupération des règles existantes');
|
|
let result = await DatabaseService.getTableData('rules', page, limit);
|
|
if (result && result.success && result.pagination) {
|
|
totalPages = result.pagination.totalPages || 1;
|
|
}
|
|
|
|
const FETCH_PAGE_PROGRESS_START = FETCH_PROGRESS;
|
|
const FETCH_PAGE_PROGRESS_END = 60;
|
|
const CREATE_START_PROGRESS = 60;
|
|
|
|
while (result && result.success) {
|
|
const fetchPageProgress = FETCH_PAGE_PROGRESS_START + ((page / totalPages) * (FETCH_PAGE_PROGRESS_END - FETCH_PAGE_PROGRESS_START));
|
|
|
|
onProgress?.(fetchPageProgress, `Page ${page}/${totalPages} : Récupération des règles`);
|
|
const existingRules: any[] = (await RuleService.getRules()).map((process: any) => process.processData);
|
|
const filteredRules: any[] = result.data.filter((rule: any) => !existingRules.some((existingRule: any) => existingRule.uid === rule.uid));
|
|
|
|
if (filteredRules.length === 0) {
|
|
console.debug('All rules already imported');
|
|
}
|
|
|
|
const totalFilteredRules = filteredRules.length;
|
|
for (let i = 0; i < totalFilteredRules; i++) {
|
|
console.log(`Adding rule ${filteredRules[i].name}`);
|
|
rules.push((await RuleService.createRule(filteredRules[i], DEFAULT_VALIDATOR_ID)).processData);
|
|
|
|
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
|
|
const ruleProgressIncrement = progressRange / (totalFilteredRules * totalPages);
|
|
const progress = CREATE_START_PROGRESS + ((page - 1) * totalFilteredRules + i + 1) * ruleProgressIncrement;
|
|
onProgress?.(progress, `Page ${page}/${totalPages} : Création de la règle ${i + 1}/${totalFilteredRules} - ${filteredRules[i].label}`);
|
|
}
|
|
|
|
if (!result.pagination.hasNextPage) {
|
|
break;
|
|
}
|
|
page++;
|
|
|
|
result = await DatabaseService.getTableData('rules', page, limit);
|
|
}
|
|
|
|
onProgress?.(FINAL_PROGRESS, 'Importation des règles terminée');
|
|
return rules;
|
|
}
|
|
|
|
private static async importRuleGroups(onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
|
|
console.log('Importing rule groups');
|
|
const ruleGroups: any[] = [];
|
|
|
|
const INIT_PROGRESS = 0;
|
|
const FETCH_PROGRESS = 30;
|
|
const CREATE_END_PROGRESS = 90;
|
|
const FINAL_PROGRESS = 100;
|
|
onProgress?.(INIT_PROGRESS, 'Initialisation');
|
|
|
|
let page = 1;
|
|
let limit = 10;
|
|
let totalPages = 1;
|
|
|
|
onProgress?.(FETCH_PROGRESS, 'Récupération des groupes de règles existants');
|
|
let result = await DatabaseService.getTableData('rules_groups', page, limit);
|
|
if (result && result.success && result.pagination) {
|
|
totalPages = result.pagination.totalPages || 1;
|
|
}
|
|
|
|
const FETCH_PAGE_PROGRESS_START = FETCH_PROGRESS;
|
|
const FETCH_PAGE_PROGRESS_END = 60;
|
|
const CREATE_START_PROGRESS = 60;
|
|
|
|
while (result && result.success) {
|
|
const fetchPageProgress = FETCH_PAGE_PROGRESS_START + ((page / totalPages) * (FETCH_PAGE_PROGRESS_END - FETCH_PAGE_PROGRESS_START));
|
|
|
|
onProgress?.(fetchPageProgress, `Page ${page}/${totalPages} : Récupération du groupe de règles`);
|
|
const existingRuleGroups: any[] = (await RuleGroupService.getRuleGroups()).map((process: any) => process.processData);
|
|
const filteredRuleGroups: any[] = result.data.filter((rule: any) => !existingRuleGroups.some((existingRule: any) => existingRule.uid === rule.uid));
|
|
|
|
const totalFilteredRuleGroups = filteredRuleGroups.length;
|
|
for (let i = 0; i < totalFilteredRuleGroups; i++) {
|
|
console.log(`Adding rule group ${filteredRuleGroups[i].name}`);
|
|
ruleGroups.push((await RuleGroupService.createRuleGroup(filteredRuleGroups[i], DEFAULT_VALIDATOR_ID)).processData);
|
|
|
|
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
|
|
const ruleProgressIncrement = progressRange / (totalFilteredRuleGroups * totalPages);
|
|
const progress = CREATE_START_PROGRESS + ((page - 1) * totalFilteredRuleGroups + i + 1) * ruleProgressIncrement;
|
|
onProgress?.(progress, `Page ${page}/${totalPages} : Création du groupe de règles ${i + 1}/${totalFilteredRuleGroups} - ${filteredRuleGroups[i].label}`);
|
|
}
|
|
|
|
if (!result.pagination.hasNextPage) {
|
|
break;
|
|
}
|
|
page++;
|
|
|
|
result = await DatabaseService.getTableData('rules_groups', page, limit);
|
|
}
|
|
|
|
onProgress?.(FINAL_PROGRESS, 'Importation des groupes de règles terminée');
|
|
return ruleGroups;
|
|
}
|
|
|
|
private static async importRoles(onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
|
|
console.log('Importing roles');
|
|
// Constantes de progression - pourraient être paramétrées
|
|
const INIT_PROGRESS = 0;
|
|
const FETCH_PROGRESS = 30;
|
|
const CREATE_START_PROGRESS = FETCH_PROGRESS;
|
|
const CREATE_END_PROGRESS = 90;
|
|
const FINAL_PROGRESS = 100;
|
|
|
|
onProgress?.(INIT_PROGRESS, 'Initialisation');
|
|
return await new Promise<any[]>((resolve: (roles: any[]) => void) => {
|
|
const defaultRoles: any[] = [
|
|
{
|
|
name: 'super-admin',
|
|
label: 'Super administrateur'
|
|
},
|
|
{
|
|
name: 'admin',
|
|
label: 'Administrateur'
|
|
},
|
|
{
|
|
name: 'notary',
|
|
label: 'Notaire'
|
|
},
|
|
{
|
|
name: 'default',
|
|
label: 'Utilisateur'
|
|
}
|
|
];
|
|
RoleService.getRoles().then(async (processes: any[]) => {
|
|
onProgress?.(FETCH_PROGRESS, 'Récupération des rôles existants');
|
|
const roles: any[] = processes.map((process: any) => process.processData);
|
|
if (roles.length === 0) {
|
|
const defaultRolesNames: string[] = defaultRoles.map((role: any) => role.name);
|
|
const totalRoles = defaultRoles.length;
|
|
for (let i = 0; i < totalRoles; i++) {
|
|
if (!defaultRolesNames.includes(roles[i].name)) {
|
|
roles.push((await RoleService.createRole(defaultRoles[i], DEFAULT_VALIDATOR_ID)).processData);
|
|
} else {
|
|
console.log(`Role ${defaultRoles[i].name} already exists`);
|
|
}
|
|
|
|
// Progression dynamique pendant la création des rôles
|
|
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
|
|
const progress = CREATE_START_PROGRESS + ((i + 1) / totalRoles) * progressRange;
|
|
onProgress?.(progress, `Création du rôle ${i + 1}/${totalRoles} : ${defaultRoles[i].label}`);
|
|
}
|
|
}
|
|
onProgress?.(FINAL_PROGRESS, 'Importation des rôles terminée');
|
|
resolve(roles);
|
|
});
|
|
});
|
|
}
|
|
|
|
private static async importDefaultRoles(officeUid: string, ruleGroups: any[], onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
|
|
console.log('Importing default roles');
|
|
let officeRoles: any[] = [];
|
|
|
|
const CREATE_START_PROGRESS = 60;
|
|
const CREATE_END_PROGRESS = 90;
|
|
|
|
onProgress?.(CREATE_START_PROGRESS, 'Création des rôles par défaut');
|
|
|
|
// Prepare the collaborator rules from rule groups
|
|
const collaboratorRules: any[] = ruleGroups
|
|
.map((ruleGroup: any) => ruleGroup.rules || [])
|
|
.reduce((acc: any, curr: any) => [...acc, ...curr], [])
|
|
.map((rule: any) => ({ uid: rule.uid }));
|
|
|
|
console.log(`Found ${collaboratorRules.length} collaborator rules from ${ruleGroups.length} rule groups`);
|
|
|
|
// Get fresh list of existing roles (including ones we just created)
|
|
const updatedExistingRoles = await OfficeRoleService.getOfficeRoles();
|
|
const existingRoles = updatedExistingRoles
|
|
.map((role: any) => role.processData)
|
|
.filter((roleData: any) => roleData.office?.uid === officeUid);
|
|
|
|
const existingRoleNames = existingRoles.map((role: any) => role.name);
|
|
|
|
const missingMandatoryRoles = mandatoryRoles.filter(roleName =>
|
|
!existingRoleNames.includes(roleName)
|
|
);
|
|
|
|
console.log(`Found ${existingRoleNames.length} existing roles, ${missingMandatoryRoles.length} mandatory roles missing`);
|
|
|
|
if (missingMandatoryRoles.length === 0) {
|
|
onProgress?.(CREATE_END_PROGRESS, 'Tous les rôles obligatoires existent déjà');
|
|
return officeRoles;
|
|
}
|
|
|
|
for (let i = 0; i < missingMandatoryRoles.length; i++) {
|
|
const roleName = missingMandatoryRoles[i];
|
|
const fallbackRole = {
|
|
name: roleName,
|
|
office: { uid: officeUid },
|
|
// Only Notaire gets rules, Collaborateur gets none
|
|
...(roleName === 'Notaire' && { rules: collaboratorRules })
|
|
};
|
|
|
|
console.log(`Creating missing mandatory role: ${roleName}`);
|
|
|
|
officeRoles.push((await OfficeRoleService.createOfficeRole(fallbackRole, DEFAULT_VALIDATOR_ID)).processData);
|
|
|
|
const progressRange = CREATE_END_PROGRESS - CREATE_START_PROGRESS;
|
|
const progress = CREATE_START_PROGRESS + ((i + 1) / missingMandatoryRoles.length) * progressRange;
|
|
onProgress?.(progress, `Création rôle obligatoire ${i + 1}/${missingMandatoryRoles.length} - ${roleName}`);
|
|
}
|
|
|
|
return officeRoles;
|
|
}
|
|
|
|
private static async importOfficeRoles(office: any, ruleGroups: any[], onProgress?: (progress: number, description?: string) => void): Promise<any[]> {
|
|
console.log('Importing office roles');
|
|
let officeRoles: any[] = [];
|
|
const officeUid = office.processData?.uid;
|
|
if (!officeUid) {
|
|
console.error('Office UID is not set');
|
|
return officeRoles;
|
|
}
|
|
console.log(`Processing ${ruleGroups.length} rule groups for office ${officeUid}`);
|
|
|
|
const INIT_PROGRESS = 0;
|
|
const FETCH_PROGRESS = 30;
|
|
const CREATE_END_PROGRESS = 90;
|
|
const FINAL_PROGRESS = 100;
|
|
onProgress?.(INIT_PROGRESS, 'Initialisation');
|
|
|
|
let page = 1;
|
|
let limit = 10;
|
|
let totalPages = 1;
|
|
|
|
onProgress?.(FETCH_PROGRESS, 'Récupération des rôles d\'office existants');
|
|
let result = await DatabaseService.getTableData('office_roles', page, limit);
|
|
if (result && result.success && result.pagination) {
|
|
totalPages = result.pagination.totalPages || 1;
|
|
}
|
|
|
|
const FETCH_PAGE_PROGRESS_START = FETCH_PROGRESS;
|
|
const FETCH_PAGE_PROGRESS_END = 60;
|
|
const CREATE_START_PROGRESS = 60;
|
|
|
|
// Collect all office roles for this office from all pages
|
|
const allOfficeRolesForThisOffice: any[] = [];
|
|
while (result && result.success) {
|
|
const fetchPageProgress = FETCH_PAGE_PROGRESS_START + ((page / totalPages) * (FETCH_PAGE_PROGRESS_END - FETCH_PAGE_PROGRESS_START));
|
|
onProgress?.(fetchPageProgress, `Page ${page}/${totalPages} : Récupération des rôles d'office`);
|
|
|
|
// Collect office roles for this office from current page
|
|
const officeRolesFromPage = result.data.filter((officeRole: any) =>
|
|
officeRole.office?.uid === officeUid
|
|
);
|
|
allOfficeRolesForThisOffice.push(...officeRolesFromPage);
|
|
|
|
if (!result.pagination.hasNextPage) {
|
|
break;
|
|
}
|
|
page++;
|
|
result = await DatabaseService.getTableData('office_roles', page, limit);
|
|
}
|
|
|
|
console.log(`Found ${allOfficeRolesForThisOffice.length} office roles in database for this office`);
|
|
|
|
if (allOfficeRolesForThisOffice.length === 0) {
|
|
console.log('No office roles found in database, creating defaults');
|
|
return await this.importDefaultRoles(officeUid, ruleGroups, onProgress);
|
|
}
|
|
|
|
// Get all existing office role processes (to avoid duplicates)
|
|
const existingOfficeRoles: any[] = await OfficeRoleService.getOfficeRoles();
|
|
const existingOfficeRoleUids = existingOfficeRoles.map((existingRole: any) =>
|
|
existingRole.processData.uid
|
|
);
|
|
|
|
console.log(`Found ${existingOfficeRoles.length} existing office role processes`);
|
|
|
|
// Import all office roles found in database (if not already imported)
|
|
const dbRolesToImport = allOfficeRolesForThisOffice.filter((dbRole: any) =>
|
|
!existingOfficeRoleUids.includes(dbRole.uid)
|
|
);
|
|
|
|
console.log(`Importing ${dbRolesToImport.length} new office roles from database`);
|
|
|
|
for (let i = 0; i < dbRolesToImport.length; i++) {
|
|
const roleData = dbRolesToImport[i];
|
|
|
|
// Ensure office UID is set correctly
|
|
if (!roleData.office) {
|
|
roleData.office = { uid: officeUid };
|
|
} else if (!roleData.office.uid) {
|
|
roleData.office.uid = officeUid;
|
|
}
|
|
|
|
console.log(`Importing office role: ${roleData.name}`);
|
|
|
|
officeRoles.push((await OfficeRoleService.createOfficeRole(roleData, DEFAULT_VALIDATOR_ID)).processData);
|
|
|
|
const progressRange = (CREATE_END_PROGRESS - CREATE_START_PROGRESS) * 0.7; // 70% for db imports
|
|
const progress = CREATE_START_PROGRESS + ((i + 1) / dbRolesToImport.length) * progressRange;
|
|
onProgress?.(progress, `Import base de données ${i + 1}/${dbRolesToImport.length} - ${roleData.name}`);
|
|
}
|
|
|
|
// Now check for mandatory roles and create any missing ones
|
|
const mandatoryRolesProgress = CREATE_START_PROGRESS + (CREATE_END_PROGRESS - CREATE_START_PROGRESS) * 0.7;
|
|
onProgress?.(mandatoryRolesProgress, 'Vérification des rôles obligatoires');
|
|
|
|
const defaultRolesCreated = await this.importDefaultRoles(officeUid, ruleGroups, (subProgress, description) => {
|
|
// Map sub-progress to remaining 20% of total progress
|
|
const mappedProgress = mandatoryRolesProgress + (subProgress - 60) * 0.3 / 30; // Scale 60-90 to remaining 30%
|
|
onProgress?.(mappedProgress, description);
|
|
});
|
|
|
|
officeRoles.push(...defaultRolesCreated);
|
|
|
|
onProgress?.(FINAL_PROGRESS, 'Importation des rôles d\'office terminée');
|
|
return officeRoles;
|
|
}
|
|
}
|