Last active
September 17, 2021 18:50
-
-
Save dcinzona/6de33517240965d080612c43fe02a945 to your computer and use it in GitHub Desktop.
Search XML for Salesforce Fields (Flows)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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