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 { // 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 { 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 { 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((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 { 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 { 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 { 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((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 { 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 { 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; } }