import { v4 as uuidv4 } from 'uuid'; import User from 'src/sdk/User'; import AbstractService from './AbstractService'; import OfficeService from './OfficeService'; import RoleService from './RoleService'; import OfficeRoleService from './OfficeRoleService'; export default class CollaboratorService extends AbstractService { private constructor() { super(); } public static createCollaborator(collaboratorData: any, validatorId: string): Promise { const ownerId: string = User.getInstance().getPairingId()!; const processData: any = { uid: uuidv4(), utype: 'collaborator', isDeleted: 'false', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), ...collaboratorData }; 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], validatorId], 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: (processCreated: any) => 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((_stateValidated: any) => { this.getCollaboratorByUid(processCreated.processData.uid).then(resolve).catch(reject); }).catch(reject); }).catch(reject); }).catch(reject); }); } public static getCollaborators(callback: (processes: any[]) => void, waitForAll: boolean = false): void { // Check if we have valid cache const items: any[] = this.getItems('_collaborators_'); if (items.length > 0 && !waitForAll) { setTimeout(() => callback([...items]), 0); } this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['utype'] && publicValues['utype'] === 'collaborator' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false' && !items.map((item: any) => item.processData.uid).includes(publicValues['uid']) ).then(async (processes: any[]) => { if (processes.length === 0) { if (waitForAll) { callback([...items]); } return; } const updatedItems: any[] = [...items]; for (let processIndex = 0; processIndex < processes.length; processIndex++) { let process = processes[processIndex]; if (!waitForAll) { process = await this.completeCollaborator(process, (processInProgress: any) => { const currentItems: any[] = [...updatedItems]; const existingIndex: number = currentItems.findIndex(item => item.processData?.uid === processInProgress.processData?.uid); if (existingIndex >= 0) { currentItems[existingIndex] = processInProgress; } else { currentItems.push(processInProgress); } callback(currentItems); }); } else { process = await this.completeCollaborator(process); } // Update cache this.setItem('_collaborators_', process); const existingIndex: number = updatedItems.findIndex(item => item.processData?.uid === process.processData?.uid); if (existingIndex >= 0) { updatedItems[existingIndex] = process; } else { updatedItems.push(process); } if (!waitForAll) { callback([...updatedItems]); } } if (waitForAll) { callback([...updatedItems]); } }); } public static getCollaboratorsBy(whereClause: { [path: string]: any }): Promise { return new Promise((resolve: (collaborators: any[]) => void) => { this.getCollaborators((processes: any[]) => { if (processes.length === 0) { resolve([]); } else { resolve(processes.filter((process: any) => { const collaborator: any = process.processData; for (const path in whereClause) { const paths: string[] = path.split('.'); let value: any = collaborator; for (let i = 0; i < paths.length; i++) { const currentPath = paths[i]; if (!currentPath || value === undefined || value === null) { break; } value = value[currentPath]; } if (value !== whereClause[path]) { return false; } } return true; })); } }, true); }); } public static getCollaboratorBy(whereClause: { [path: string]: any }): Promise { return new Promise((resolve: (collaborator: any | null) => void) => { this.getCollaborators((processes: any[]) => { if (processes.length === 0) { resolve(null); } else { resolve(processes.find((process: any) => { const collaborator: any = process.processData; for (const path in whereClause) { const paths: string[] = path.split('.'); let value: any = collaborator; for (let i = 0; i < paths.length; i++) { const currentPath = paths[i]; if (!currentPath || value === undefined || value === null) { break; } value = value[currentPath]; } if (value !== whereClause[path]) { return false; } } return true; })); } }, true); }); } public static getCollaboratorByUid(uid: string, forceRefresh: boolean = false): Promise { // Check if we have valid cache const item: any = this.getItem('_collaborators_', uid); if (item && !forceRefresh) { return Promise.resolve(item); } return new Promise((resolve: (process: any) => void, reject: (error: string) => void) => { this.messageBus.getProcessesDecoded((publicValues: any) => publicValues['uid'] && publicValues['uid'] === uid && publicValues['utype'] && publicValues['utype'] === 'collaborator' && publicValues['isDeleted'] && publicValues['isDeleted'] === 'false' ).then(async (processes: any[]) => { if (processes.length === 0) { resolve(null); } else { let process: any = processes[0]; process = await this.completeCollaborator(process); // Update cache this.setItem('_collaborators_', process); resolve(process); } }).catch(reject); }); } public static updateCollaborator(process: any, newData: any): Promise { return new Promise((resolve: () => void, reject: (error: string) => void) => { this.messageBus.updateProcess(process.processId, { updated_at: new Date().toISOString(), ...newData }, [], null).then((processUpdated: any) => { const newStateId: string = processUpdated.diffs[0]?.state_id; this.messageBus.notifyUpdate(process.processId, newStateId).then(() => { this.messageBus.validateState(process.processId, newStateId).then((_stateValidated) => { const collaboratorUid: string = process.processData.uid; this.removeItem('_collaborators_', collaboratorUid); this.getCollaboratorByUid(collaboratorUid, true).then(resolve).catch(reject); }).catch(reject); }).catch(reject); }).catch(reject); }); } private static async completeCollaborator(process: any, progressCallback?: (processInProgress: any) => void): Promise { const progressiveProcess: any = JSON.parse(JSON.stringify(process)); if (process.processData.office) { const office: any = (await OfficeService.getOfficeByUid(process.processData.office.uid)).processData; process.processData.office = { uid: office.uid, idNot: office.idNot, crpcen: office.crpcen, name: office.name, office_status: office.office_status }; if (progressCallback) { progressiveProcess.processData.office = process.processData.office; progressCallback(JSON.parse(JSON.stringify(progressiveProcess))); } } if (process.processData.role) { const role: any = (await RoleService.getRoleByUid(process.processData.role.uid)).processData; process.processData.role = { uid: role.uid, name: role.name }; if (progressCallback) { progressiveProcess.processData.role = process.processData.role; progressCallback(JSON.parse(JSON.stringify(progressiveProcess))); } } if (process.processData.office_role) { const officeRole: any = (await OfficeRoleService.getOfficeRoleByUid(process.processData.office_role.uid)).processData; process.processData.office_role = { uid: officeRole.uid, name: officeRole.name, rules: officeRole.rules?.map((rule: any) => { return { uid: rule.uid, name: rule.name }; }) }; if (progressCallback) { progressiveProcess.processData.office_role = process.processData.office_role; progressCallback(JSON.parse(JSON.stringify(progressiveProcess))); } } return process; } }