import envPaths from 'env-paths';
import path from 'path';
import fse from 'fs-extra';
import eventHandler from '../../../frontend/backend-api/event-system/EventHandler.js';
import { getProcessInfo as parseProcessInfo } from '../../../shared-frontend-backend/helpers/processHelpers.js';
// Exposes functions to find directories and get or store data
/**
* Returns the path where all the data for the MS is stored
*
* should be:
*
* Server:
* Production: same directory as server files
* Development: /path/to/appdata-directory/proceed-management-system-development
* Electron:
* Production: /path/to/appdata-directory/proceed-management-system
* Development: /path/to/appdata-directory/proceed-management-system-development
*
* @returns {String}
*/
export function getAppDataPath() {
let appDir;
if (!process.env.IS_ELECTRON && process.env.NODE_ENV === 'production') {
appDir = __dirname;
} else {
appDir = envPaths('proceed-management-system').config;
appDir = appDir.slice(0, appDir.search('-nodejs'));
if (process.env.NODE_ENV === 'development') {
appDir = path.join(appDir, 'development');
}
}
return appDir;
}
/**
* Find the path to the folder where the info about all Processes is stored
* @returns {String}
*/
function getProcessesFolder() {
return path.join(getAppDataPath(), 'Processes');
}
/**
* Find the path to the folder where the info about all Environment Profiles is stored
* @returns {String}
*/
function getEnvFolder() {
return path.join(getAppDataPath(), 'EnvProfiles');
}
/**
* Find the path to the file where the info about an environment profile is stored
* @returns {String}
*/
function getEnvProfileName(id, type) {
const envFolder = getEnvFolder();
const ID = id.substring(0, 8);
return path.join(envFolder, 'env-'.concat(type.toLowerCase(), '-', ID, '.json'));
}
/**
* Get the json of an environment profile
*
* @returns {String} the environmentProfile json
*/
export async function getEnvProfileJSON(id, type) {
const filePath = getEnvProfileName(id, type);
return JSON.parse(fse.readFileSync(filePath));
}
/**
* Find the path to the folder where the data of a specific process is stored
*
* @param id
* @returns {String}
*/
function getFolder(id) {
const processesFolder = getProcessesFolder();
return path.join(processesFolder, id);
}
/**
* Find the user task directory for the given process
*
* @param {String} id
*/
function getUserTaskDir(id) {
return path.join(getFolder(id), 'User-Tasks');
}
/**
* Get the bpmn of a specific process as a string
* @param {String} processDefinitionsId
* @returns {String} the process description
*/
export async function getBPMN(processDefinitionsId) {
const folder = getFolder(processDefinitionsId);
const bpmnFilePath = path.join(folder, processDefinitionsId.concat('.bpmn'));
return fse.readFileSync(bpmnFilePath, 'utf-8');
}
/**
* Saves the json for a given environment profile
*
* @param {String} Id the id of the environment profile
* @param {String} type the type of the environment profile
* @param {String} environmentProfile the environment profile json
*/
export async function saveEnvProfile(id, type, environmentProfile) {
const envFolder = getEnvFolder();
// creates the directory if it doesn't exist
fse.ensureDirSync(envFolder);
const fileName = getEnvProfileName(id, type);
fse.writeFileSync(fileName, environmentProfile);
}
/**
* Removes the json file of the environment profile with the given id
*
* @param {String} id the id of the environment profile
* @param {String} type the type of the environment profile
*/
export async function deleteEnvProfile(id, type) {
const fileToRemove = getEnvProfileName(id, type);
await fse.unlinkSync(fileToRemove);
}
/**
* Saves the process bpmn of a process
*
* @param {String} id the id of the process
* @param {String} bpmn the process description
*/
export async function saveProcess(id, bpmn) {
const currentProcessFolder = getFolder(id);
fse.ensureDirSync(currentProcessFolder);
fse.writeFileSync(path.join(currentProcessFolder, id.concat('.bpmn')), bpmn);
eventHandler.dispatch('files_changed_bpmn', { processDefinitionsId: id, bpmn });
}
/**
* Deletes the directory for the process we want to remove
*
* @param {String} id
*/
export async function deleteProcess(id) {
const processDirectory = getFolder(id);
fse.removeSync(processDirectory);
}
/**
* Returns the ids of all user tasks of the process with the given id
*
* @param {String} processDefinitionsId
*/
export function getUserTaskIds(processDefinitionsId) {
return new Promise((resolve, reject) => {
const userTaskDir = getUserTaskDir(processDefinitionsId);
if (!fse.existsSync(userTaskDir)) {
resolve([]);
}
fse.readdir(userTaskDir, (err, files) => {
if (err) {
reject(err);
return;
}
const userTaskIds = [];
if (files) {
files.forEach(async (file) => {
const htmlFilePath = path.join(userTaskDir, file);
const htmlFileContents = fse.readFileSync(htmlFilePath, 'utf-8');
const [taskId] = file.split('.');
userTaskIds.push(taskId);
});
}
resolve(userTaskIds);
});
});
}
/**
* Saves the html of the a user task
*
* @param {String} processDefinitionsId the id of the process that contains the user task
* @param {String} taskId the id of the specific user task
* @param {String} html the html data of the user task
*/
export async function saveUserTaskHTML(processDefinitionsId, taskId, html) {
const userTaskDir = getUserTaskDir(processDefinitionsId);
fse.ensureDirSync(userTaskDir);
fse.writeFileSync(path.join(userTaskDir, `${taskId}.html`), html);
eventHandler.dispatch('files_changed_html', { processDefinitionsId, taskId, html });
}
/**
* Returns the html for a user task with the given id in a process
*
* @param {String} processDefinitionsId
* @param {String} taskId
*/
export async function getUserTaskHTML(processDefinitionsId, taskId) {
const userTaskDir = getUserTaskDir(processDefinitionsId);
const userTaskFile = `${taskId}.html`;
const userTaskPath = path.join(userTaskDir, userTaskFile);
return fse.readFileSync(userTaskPath, 'utf-8');
}
/**
* Returns the html for all user tasks in a process
*
* @param {String} processDefinitionsId
*
* @returns {Promise}
* @resolves {Object} Object containing a taskId to task html mapping
*/
export function getUserTasksHTML(processDefinitionsId) {
return new Promise((resolve, reject) => {
const userTaskDir = getUserTaskDir(processDefinitionsId);
if (!fse.existsSync(userTaskDir)) {
resolve({});
}
fse.readdir(userTaskDir, (err, files) => {
if (err) {
reject(err);
return;
}
const userTasksHTML = {};
if (files) {
files.forEach(async (file) => {
const htmlFilePath = path.join(userTaskDir, file);
const htmlFileContents = fse.readFileSync(htmlFilePath, 'utf-8');
const [taskId] = file.split('.');
userTasksHTML[taskId] = htmlFileContents;
});
}
resolve(userTasksHTML);
});
});
}
export async function deleteUserTaskHTML(processDefinitionsId, taskId) {
const userTaskDir = getUserTaskDir(processDefinitionsId);
const taskFile = `${taskId}.html`;
const filePath = path.join(userTaskDir, taskFile);
fse.unlinkSync(filePath);
}
/**
* Saves the script for a scriptTask
*
* @param {String} processDefinitionsId
* @param {String} taskId
* @param {String} js
*/
export async function saveScriptTaskJS(processDefinitionsId, taskId, js) {
const currentProcessFolder = getFolder(processDefinitionsId);
const folder = path.join(currentProcessFolder, 'Script-Tasks');
fse.ensureDirSync(folder);
fse.writeFileSync(path.join(folder, `${taskId}.js`), js);
}
/**
* Saves new process bpmn for an existing process and changes the file and directory name if the process name changed
*
* @param {String} id
* @param {String} bpmn
* @param {String} newName optional
*/
export async function updateProcess(id, bpmn) {
// Save new bpmn into existing bpmn file
const currentProcessFolderPath = getFolder(id);
const currentBpmnFile = path.join(currentProcessFolderPath, id.concat('.bpmn'));
fse.writeFileSync(currentBpmnFile, bpmn);
eventHandler.dispatch('files_changed_bpmn', { processDefinitionsId: id, bpmn });
}
export async function getAllProcessesBpmn() {
let bpmnFiles = {};
let dirEntries;
const processesFolder = getProcessesFolder();
// create the processes directory if it doesn't exist yet
fse.ensureDirSync(processesFolder);
try {
dirEntries = await fse.readdir(processesFolder, { withFileTypes: true });
} catch (error) {
//logger is not yet possible to include here
console.error('Error during the attempt to read the Processes directory: \n' + error);
throw new Error('Failed to read Processes directory');
}
// Look into every process folder an try to get the BPMN
for (const dirEntry of dirEntries) {
if (dirEntry.isDirectory()) {
const bpmnFilePath = path.join(processesFolder, dirEntry.name, `${dirEntry.name}.bpmn`);
try {
const bpmnFileContents = await fse.readFile(bpmnFilePath, 'utf-8');
bpmnFiles[dirEntry.name] = bpmnFileContents;
} catch (error) {
//logger is not yet possible to include here
console.info(
'A BPMN process file does not exists in a process folder. Process is skipped.\n' + error
);
}
}
}
return bpmnFiles;
}
export async function getUpdatedProcessesJSON(processes) {
const processesMapping = await getAllProcessesBpmn();
let newProcesses = [];
Object.keys(processesMapping).forEach((definitionId) => {
const existingProcess = processes.find((p) => p.id === definitionId);
const newProcess = getProcessInfo(processesMapping[definitionId], existingProcess || {});
newProcesses.push(newProcess);
});
return await Promise.all(newProcesses);
}
async function getProcessInfo(bpmn, process) {
const { id, originalId, name, processIds, description, subprocesses } = await parseProcessInfo(
bpmn
);
const currentDate = new Date().toUTCString();
let newProcess = {
id,
originalId,
name,
description,
departments: process.departments || [],
variables: process.variables || [],
subprocesses,
createdOn: process.createdOn || currentDate,
lastEdited: process.lastEdited || currentDate,
type: process.type || (process.isProject ? 'project' : 'process'),
processIds,
owner: process.owner,
};
if (newProcess.type === 'project') {
newProcess = {
...newProcess,
scheduleStatus: process.scheduleStatus || '',
planningStatus: process.planningStatus || '',
plannedEndDate: process.plannedEndDate || '',
};
}
return newProcess;
}