@dxatscale/sfprofiles
Version:
Salesforce Profile management
347 lines (315 loc) • 12.8 kB
text/typescript
import * as fs from 'fs-extra';
import * as xml2js from 'xml2js';
import * as util from 'util';
import DiffUtil from './diffUtil';
const _ = require('lodash');
const parser = new xml2js.Parser({
explicitArray: false,
valueProcessors: [
function (name) {
let result: boolean;
if (name === 'true') result = true;
if (name === 'false') result = false;
return result;
},
],
});
export default class WorkflowDiff {
public static async generateWorkflowXml(
workflowXml1: string,
workflowXml2: string,
outputFilePath: string,
objectName: string,
destructivePackageObj: any[],
resultOutput: any[],
isDestructive: boolean
) {
let workflowObj1: any = {};
let workflowObj2: any = {};
const parseString = util.promisify(parser.parseString) as _.Function1<xml2js.convertableToString, Promise<any>>;
if (workflowXml1 !== '') {
let parseResult = await parseString(workflowXml1);
workflowObj1 = parseResult.Workflow || {};
}
if (workflowXml2 !== '') {
let parseResult = await parseString(workflowXml2);
workflowObj2 = parseResult.Workflow || {};
}
let addedEditedOrDeleted = WorkflowDiff.buildNewWorkflowObj(workflowObj1, workflowObj2);
WorkflowDiff.writeWorkflow(addedEditedOrDeleted.addedEdited, outputFilePath);
destructivePackageObj = WorkflowDiff.buildDestructiveChangesObj(
addedEditedOrDeleted.deleted,
destructivePackageObj,
objectName
);
WorkflowDiff.updateOutput(addedEditedOrDeleted.addedEdited, resultOutput, objectName, 'Deploy', outputFilePath);
if (isDestructive) {
WorkflowDiff.updateOutput(
addedEditedOrDeleted.deleted,
resultOutput,
objectName,
'Delete',
'destructiveChanges.xml'
);
}
return destructivePackageObj;
}
private static updateOutput(workflowObj, resultOutput: any[], objectName, action, filePath) {
workflowObj.alerts.forEach((elem) => {
resultOutput.push({
action: action,
metadataType: 'WorkflowAlert',
componentName: `${objectName}.${elem.fullName}`,
path: filePath,
});
});
workflowObj.fieldUpdates.forEach((elem) => {
resultOutput.push({
action: action,
metadataType: 'WorkflowFieldUpdate',
componentName: `${objectName}.${elem.fullName}`,
path: filePath,
});
});
workflowObj.knowledgePublishes.forEach((elem) => {
resultOutput.push({
action: action,
metadataType: 'WorkflowKnowledgePublish',
componentName: `${objectName}.${elem.label}`,
path: filePath,
});
});
workflowObj.outboundMessages.forEach((elem) => {
resultOutput.push({
action: action,
metadataType: 'WorkflowOutboundMessage',
componentName: `${objectName}.${elem.fullName}`,
path: filePath,
});
});
workflowObj.rules.forEach((elem) => {
resultOutput.push({
action: action,
metadataType: 'WorkflowRule',
componentName: `${objectName}.${elem.fullName}`,
path: filePath,
});
});
workflowObj.tasks.forEach((elem) => {
resultOutput.push({
action: action,
metadataType: 'WorkflowTask',
componentName: `${objectName}.${elem.fullName}`,
path: filePath,
});
});
}
private static ensureArray(workflowObj) {
let keys = Object.keys(workflowObj);
keys.forEach((key) => {
if (typeof workflowObj[key] === 'object' && !Array.isArray(workflowObj[key]) && key !== '$') {
workflowObj[key] = [workflowObj[key]];
}
});
return workflowObj;
}
public static async getMembers(filePath: string) {
let fileContent = fs.readFileSync(filePath, 'utf8').toString();
const parseString = util.promisify(parser.parseString) as _.Function1<xml2js.convertableToString, Promise<any>>;
let members = {};
if (fileContent !== '') {
let parseResult = await parseString(fileContent);
let workFlowObj = parseResult.Workflow || {};
if (!_.isNil(workFlowObj.alerts)) {
if (!Array.isArray(workFlowObj.alerts)) {
members['WorkflowAlert'] = [workFlowObj.alerts.fullName];
} else {
members['WorkflowAlert'] = workFlowObj.alerts.map((workFlowAlert) => {
return workFlowAlert.fullName;
});
}
}
if (!_.isNil(workFlowObj.fieldUpdates)) {
if (!Array.isArray(workFlowObj.fieldUpdates)) {
members['WorkflowFieldUpdate'] = [workFlowObj.fieldUpdates.fullName];
} else {
members['WorkflowFieldUpdate'] = workFlowObj.fieldUpdates.map((workFlowFU) => {
return workFlowFU.fullName;
});
}
}
if (!_.isNil(workFlowObj.knowledgePublishes)) {
if (!Array.isArray(workFlowObj.knowledgePublishes)) {
members['WorkflowKnowledgePublish'] = [workFlowObj.knowledgePublishes.label];
} else {
members['WorkflowKnowledgePublish'] = workFlowObj.knowledgePublishes.map(
(workflowKnowledgePublish) => {
return workflowKnowledgePublish.label;
}
);
}
}
if (!_.isNil(workFlowObj.outboundMessages)) {
if (!Array.isArray(workFlowObj.outboundMessages)) {
members['WorkflowOutboundMessage'] = [workFlowObj.outboundMessages.fullName];
} else {
members['WorkflowOutboundMessage'] = workFlowObj.outboundMessages.map((workflowOutboundMessage) => {
return workflowOutboundMessage.fullName;
});
}
}
if (!_.isNil(workFlowObj.rules)) {
if (!Array.isArray(workFlowObj.rules)) {
members['WorkflowRule'] = [workFlowObj.rules.fullName];
} else {
members['WorkflowRule'] = workFlowObj.rules.map((workflowRule) => {
return workflowRule.fullName;
});
}
}
if (!_.isNil(workFlowObj.tasks)) {
if (!Array.isArray(workFlowObj.tasks)) {
members['WorkflowTask'] = [workFlowObj.tasks.fullName];
} else {
members['WorkflowTask'] = workFlowObj.tasks.map((workflowTask) => {
return workflowTask.fullName;
});
}
}
}
return members;
}
private static buildNewWorkflowObj(workflowObj1: any, workflowObj2: any) {
workflowObj1 = WorkflowDiff.ensureArray(workflowObj1);
workflowObj2 = WorkflowDiff.ensureArray(workflowObj2);
let newWorkflowObj: any = {
$: { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
alerts: [],
fieldUpdates: [],
knowledgePublishes: [],
outboundMessages: [],
rules: [],
tasks: [],
};
let deletedWorkflowObj: any = {
$: { xmlns: 'http://soap.sforce.com/2006/04/metadata' },
alerts: [],
fieldUpdates: [],
knowledgePublishes: [],
outboundMessages: [],
rules: [],
tasks: [],
};
if (workflowObj1.fullName !== undefined || workflowObj2.fullName !== undefined) {
newWorkflowObj.fullName = workflowObj2.fullName;
}
//Email alerts
let addedDeleted = DiffUtil.getChangedOrAdded(workflowObj1.alerts, workflowObj2.alerts, 'fullName');
newWorkflowObj.alerts = addedDeleted.addedEdited;
deletedWorkflowObj.alerts = addedDeleted.deleted;
//Field Update
addedDeleted = DiffUtil.getChangedOrAdded(workflowObj1.fieldUpdates, workflowObj2.fieldUpdates, 'fullName');
newWorkflowObj.fieldUpdates = addedDeleted.addedEdited;
deletedWorkflowObj.fieldUpdates = addedDeleted.deleted;
//Knowledge Publishes
addedDeleted = DiffUtil.getChangedOrAdded(
workflowObj1.knowledgePublishes,
workflowObj2.knowledgePublishes,
'label'
);
newWorkflowObj.knowledgePublishes = addedDeleted.addedEdited;
deletedWorkflowObj.knowledgePublishes = addedDeleted.deleted;
//Outbound Messages
addedDeleted = DiffUtil.getChangedOrAdded(
workflowObj1.outboundMessages,
workflowObj2.outboundMessages,
'fullName'
);
newWorkflowObj.outboundMessages = addedDeleted.addedEdited;
deletedWorkflowObj.outboundMessages = addedDeleted.deleted;
//Rules
addedDeleted = DiffUtil.getChangedOrAdded(workflowObj1.rules, workflowObj2.rules, 'fullName');
newWorkflowObj.rules = addedDeleted.addedEdited;
deletedWorkflowObj.rules = addedDeleted.deleted;
//Task
addedDeleted = DiffUtil.getChangedOrAdded(workflowObj1.tasks, workflowObj2.tasks, 'fullName');
newWorkflowObj.tasks = addedDeleted.addedEdited;
deletedWorkflowObj.tasks = addedDeleted.deleted;
return {
addedEdited: newWorkflowObj,
deleted: deletedWorkflowObj,
};
}
private static buildDestructiveChangesObj(deletedWorkflows: any, destructivePackageObj: any[], objectName: string) {
destructivePackageObj = WorkflowDiff.buildDestructiveType(
destructivePackageObj,
deletedWorkflows.alerts,
'WorkflowAlert',
objectName
);
destructivePackageObj = WorkflowDiff.buildDestructiveType(
destructivePackageObj,
deletedWorkflows.fieldUpdates,
'WorkflowFieldUpdate',
objectName
);
destructivePackageObj = WorkflowDiff.buildDestructiveType(
destructivePackageObj,
deletedWorkflows.knowledgePublishes,
'WorkflowKnowledgePublish',
objectName
);
destructivePackageObj = WorkflowDiff.buildDestructiveType(
destructivePackageObj,
deletedWorkflows.outboundMessages,
'WorkflowOutboundMessage',
objectName
);
destructivePackageObj = WorkflowDiff.buildDestructiveType(
destructivePackageObj,
deletedWorkflows.rules,
'WorkflowRule',
objectName
);
destructivePackageObj = WorkflowDiff.buildDestructiveType(
destructivePackageObj,
deletedWorkflows.tasks,
'WorkflowTask',
objectName
);
return destructivePackageObj;
}
private static buildDestructiveType(
destructivePackageObj: any[],
list: any[],
typeLabel: string,
objectName: string
) {
let metaType: any = _.find(destructivePackageObj, function (metaType: any) {
return metaType.name === typeLabel;
});
if (metaType === undefined && list !== undefined && list.length > 0) {
metaType = {
name: typeLabel,
members: [],
};
destructivePackageObj.push(metaType);
}
if (list !== undefined) {
list.forEach((elem) => {
metaType.members.push(objectName + '.' + elem.fullName);
});
}
return destructivePackageObj;
}
private static writeWorkflow(newWorkflowObj: any, outputFilePath: string) {
const builder = new xml2js.Builder({
xmldec: { version: '1.0', encoding: 'UTF-8', standalone: null },
});
let workflowObj = {
Workflow: newWorkflowObj,
};
let xml = builder.buildObject(workflowObj);
fs.writeFileSync(outputFilePath, xml);
}
}