Skip to content

Instantly share code, notes, and snippets.

@dcinzona
Last active September 17, 2021 18:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcinzona/6de33517240965d080612c43fe02a945 to your computer and use it in GitHub Desktop.
Save dcinzona/6de33517240965d080612c43fe02a945 to your computer and use it in GitHub Desktop.
Search XML for Salesforce Fields (Flows)
#!/bin/sh
# Purpose: Parse Salesforce XML / Flows to find fields referenced in an input CSV
# Fields CSV Format:
# sobject,fieldApi,ObjectLabel,FieldLabel,PerformFunctionalAssessment
# Author: Gustavo Tandeciarz
# Requires: xmlstarlet (brew install xmlstarlet)
# execution to parse flows: ./findFields.sh fields.csv ./src/flows
# ------------------------------------------
INPUT=$1
FLOWS_DIR=$2
echo "Checking xml files in $FLOWS_DIR. This may take a while..."
OLDIFS=$IFS
IFS=','
[ ! -f $INPUT ] && { echo "$INPUT file not found"; exit 99; }
FLOWS="$FLOWS_DIR/*"
CSVOUT="Flow,Object,Field"
function SaveAsText {
local result=$(xml sel -t --if \
"//*[name()='object' and text()='$1']/..//*[name()='field' and text()='$2']" \
-n \
-o "Found $1.$2" \
-o " in " -f -n \
$FLOWS)
if [[ ! -z "$result" ]]
then
echo "$result"
fi
}
function SaveAsCSV {
local result=$(cd $FLOWS_DIR && xml sel -t --if \
"//*[name()='object' and text()='$1']/..//*[name()='field' and text()='$2']" \
-n \
-f \
-o ",$1,$2" \
*)
if [[ ! -z "$result" ]]
then
echo "$result"
fi
#-o ",\"" \
#-c "name(//*[name()='object' and text()='$1']/..//*[name()='field' and text()='$2']/../..)" \
#-o "\"" \
}
while read sobject fieldApi ObjectLabel FieldLabel PerformFunctionalAssessment
do
printf '\33[2K'
printf "\r\e[0mChecking OBJECT: \e[1;34m$sobject\e[0m FIELD: \e[1;34m$fieldApi\e[0m"
result=$(SaveAsCSV $sobject $fieldApi)
if [[ ! -z "$result" ]]
then
CSVTEMP="$CSVTEMP$result"
#echo "$CSVTEMP"
fi
done < $INPUT
CSVOUT="Flow,Object,Field$CSVTEMP"
IFS=$OLDIFS
echo "$CSVOUT" > results.csv
// REQUIRES xmlstarlet
// brew install xmlstarlet
var fs = require('fs');
var parse = require('csv-parse');
const {resolve} = require("path");
var myArgs = process.argv.slice(2);
var csvData=[];
var dataToCheck=[];
var filesArray = myArgs.slice(1);
var fullPathArray = [];
for(f of filesArray){
let fullPath = resolve(f);
fullPathArray.push(fullPath);
}
var filesJoined = fullPathArray.join(' ');
var file = fullPathArray[0];
var dir = '';
const responseData =['FLOW,SOBJECT_API_NAME,FIELD_API_NAME,REFERENCE_STRING,ELEMENT_DATA'];
function popDataToCheck(data){
const mySet1 = new Set();
for(rowData of data){
let sobj = rowData.sobject;
if(sobj.endsWith('__c')){
rowData.relationshipString = sobj.replace(/.$/,"r." + rowData.fieldApi);
} else {
rowData.relationshipString = null;
}
dataToCheck.push(rowData);
}
processData();
}
let replacer = new RegExp(dir, 'g');
function processData(){
console.log(`Processing ${COLOR.fgYellow} ${fullPathArray.length} ${COLOR.reset} file(s) and ${COLOR.fgYellow} ${dataToCheck.length} ${COLOR.reset}fields\n`)
const { execSync } = require('child_process');
dataToCheck.forEach((row,idx) => {
let prg = (`Percent Complete: ${COLOR.fgCyan} ${(idx / dataToCheck.length).toFixed(2) * 100}\% ${COLOR.reset}`);
let msg =`${COLOR.reset}Checking OBJECT: ${COLOR.fgBlue}${row.sobject} ${COLOR.reset}FIELD: ${COLOR.fgBlue}${row.fieldApi}${COLOR.reset}`;
printProgress(`${prg}\n${msg}`);
let xpath = `//*[(name()='object' and text()='${row.sobject}' and ..//*[name()='field' and text()='${row.fieldApi}'] and not(parent::inputAssignments)) `// or contains(text(),'Board_Applicant__r.FirstName')]/..`
let cmd='';
if(row.hasOwnProperty('relationshipString') && row.relationshipString != null){
xpath += ` or contains(text(),'${row.relationshipString}')]`;
} else {
row.relationshipString = '';
xpath+=']'
}
xpath += '/..'
cmd = `xml sel -I -B -t -m "${xpath}" \
--if 'position()=1' -f -o ",${row.sobject},${row.fieldApi},${row.relationshipString},[[REPLQUOTE]]" \
--else -n -n -b`;
cmd += ` -o "[[ELEMENT]]" -n -c '.' -n -o "[[/ELEMENT]]" `; //print xpath with tags for quotes
cmd += ` --if "position()=last()" -o "[[REPLQUOTE]]" `
try{
cmd+=` ${filesJoined} `;
cmd+=' 2>/dev/null';
let resultData = execSync(cmd).toString();
responseData.push(resultData);
} catch (ex){
}
});
printProgress(`${COLOR.fgGreen}DONE!${COLOR.reset}\n`);
finish();
}
function printProgress(progress){
let linesToClear = progress.indexOf('\n') == -1 ? 1 : progress.split('\n').length;
process.stdout.clearLine();
process.stdout.cursorTo(0,process.stdout.rows - linesToClear);
process.stdout.clearLine();
process.stdout.write(progress);
}
function finish(){
if(responseData.length == 0){
console.log(`Results: ${COLOR.fgRed}Nothing found${COLOR.reset}`);
} else {
csvString();
}
}
function csvString(){
let str = responseData.map((row,idx) =>{
let escp = (row.replace(/\"/g, '\'').replace(/\[\[REPLQUOTE\]\]/g,'"').trimEnd())
return (escp);
}).join('\r\n').replace(replacer,'').trimEnd();
fs.writeFileSync('results_with_xpath.csv',str)
console.log(`Results saved to ${COLOR.fgGreen} ${resolve('results_with_xpath.csv')} ${COLOR.reset}`)
}
function run(){
if(file.indexOf('/') == -1){
//this is a single file
}
else {
//contains directory
dir = file.substring(0, file.lastIndexOf('/')+1);
if(fs.lstatSync(file).isDirectory()){
dir = file;
}
console.log(`File Directory: ${COLOR.fgMagenta} ${dir} ${COLOR.reset}`);
}
replacer = new RegExp(dir, 'g');
fs.createReadStream(myArgs[0])
.pipe(parse({columns: true}))
.on('data', function(csvrow) {
//do something with csvrow
csvData.push(csvrow);
})
.on('end',function() {
//do something with csvData
popDataToCheck(csvData);
});
}
const COLOR = {
reset: '\x1b[0m',
bright: '\x1b[1m',
dim: '\x1b[2m',
underscore: '\x1b[4m',
blink: '\x1b[5m',
reverse: '\x1b[7m',
hidden: '\x1b[8m',
fgBlack: '\x1b[30m',
fgRed: '\x1b[31m',
fgGreen: '\x1b[32m',
fgYellow: '\x1b[33m',
fgBlue: '\x1b[34m',
fgMagenta: '\x1b[35m',
fgCyan: '\x1b[36m',
fgWhite: '\x1b[37m',
bgBlack: '\x1b[40m',
bgRed: '\x1b[41m',
bgGreen: '\x1b[42m',
bgYellow: '\x1b[43m',
bgBlue: '\x1b[44m',
bgMagenta: '\x1b[45m',
bgCyan: '\x1b[46m',
bgWhite: '\x1b[47m',
};
//console.log(`${COLOR.fgRed}This text is red.${COLOR.reset}`);
run();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment