CDC - How to Reconstruct a Field from Its Diff Value
CDC- Reconstruct a Field from Its Diff Value in Apex
The following code sample can be used to reconstruct a long textarea field from its Diff value in a CDC packet. I have written it as a utility method which would need you to parse the CDC JSON packet and query the current value of the field from your DB.This is going to be useful when you are using CDC to keep 2 salesforce orgs in sync. On the listener side you need to implement this as part of CDC packet consumption.
The first parameter (oldText) is the current value in the database before applying the CDC packet.
The diff string is the parsed value from the CDC packet. For a JSON Node like
"Description":{
"diff":"--- \n+++ 682b8747ccdb93b546e7bbe479b27d26ec7c38ccabb76cdd8308c6595492bffc\n@@ -2,1 +2,1 @@\n-Business applications are moving to the cloud. It’s not just a fad—the shift from traditional software models to the Internet has steadily gained momentum over the last 10 years.\n+Business apps are moving to the cloud. It’s not just a fad—the shift from traditional software models to the Internet has steadily gained momentum over the last 10 years.\n@@ -7,1 +7,1 @@\n-As cloud computing grows in popularity, thousands of companies are simply rebranding their non-cloud products and services as “cloud computing.” Always dig deeper when evaluating cloud offerings.\n+As cloud computing grows in popularity, thousands of companies are simply rebranding their non-cloud products and services as “cloud computing.” Always dig deeper when evaluating cloud offerings. And keep in mind that if you have to buy and manage hardware and software, what you’re looking at isn’t really cloud computing but a false cloud."
},
The method expects you to parse the diff out of the JSON and send the following to the method.
--- \n+++ 682b8747ccdb93b546e7bbe479b27d26ec7c38ccabb76cdd8308c6595492bffc\n@@ -2,1 +2,1 @@\n-Business applications are moving to the cloud. It’s not just a fad—the shift from traditional software models to the Internet has steadily gained momentum over the last 10 years.\n+Business apps are moving to the cloud. It’s not just a fad—the shift from traditional software models to the Internet has steadily gained momentum over the last 10 years.\n@@ -7,1 +7,1 @@\n-As cloud computing grows in popularity, thousands of companies are simply rebranding their non-cloud products and services as “cloud computing.” Always dig deeper when evaluating cloud offerings.\n+As cloud computing grows in popularity, thousands of companies are simply rebranding their non-cloud products and services as “cloud computing.” Always dig deeper when evaluating cloud offerings. And keep in mind that if you have to buy and manage hardware and software, what you’re looking at isn’t really cloud computing but a false cloud .
You can implement a custom exception to be thrown in case the generated HASH value does not match with the one in the packet.
public with sharing class generateLongTextAreaFromCDCDiff {private final String newLine = '\r\n';private final String diffSplitLineCharacter = '\\\\' + 'n@@';/*** Method to get a list of lines by spliting a string variable* into lines using a split line character** @author :Himanshoo Seth* @Since :06/03/2019* @param :String that needs to be splitted* @param :Split line character which will be used forsplitting the string* @return :List of lines splitted from one given stringon the basis of split line character**/private List<String> toLines(String s, String splitLineCharacter) {return s.split(splitLineCharacter);}/*** method to combine a list of lines into one string variable.** @author :Himanshoo Seth* @Since :06/03/2019* @param :list of lines which has to be combined into onestring variable* @param :Combine line character which will be used toreintroduce the original line breaks in the string variable* @return :A string variable obtained by combining the input list of lines**/private String combineLines(List<String> lines, String combineLineCharacter) {String updateString = lines[0];for(Integer i=1; i<lines.size(); i++){updateString = updateString + combineLineCharacter + lines[i];}return updateString;}/*** Method to generate the list of revised lines by applying the* changes from diff lines to original lines** @author :Himanshoo Seth* @Since :06/03/2019* @param :Original Message splitted into list of lines on which* the changes are to be applied* @param :Diff message splitted into list of lines, which contains* the updates* @return :List of lines generated from applying the changes from diff* lines to original lines**/private List<String> generateRevisedLines(List<String> originalLines, List<String> diffLines){List<wrapperDiffUnit> lstWrapper = parseDiffLines(diffLines);processSubtraction(lstWrapper, originalLines);processAddition(lstWrapper, originalLines);return originalLines;}/*** Method to extract the values of the coordinates and the lines to be updated* in the originals lines from the diff lines and store it in a list of wrapper Diff unit** @author :Himanshoo Seth* @Since :06/03/2019* @param :Diff message splitted into list of lines, which contains the updates* @return :List of wrapper diff unit which contains the extracted values of all* the coordinates and the lines to be updated in the originals lines**/private List<wrapperDiffUnit> parseDiffLines(List<String> diffLines) {List<wrapperDiffUnit> lstWrapper = new List<wrapperDiffUnit>();for(Integer i=1; i<diffLines.size(); i++) {//This is to get the changed coordinates and updated String//e.g. (-2,1 +2,1) ==> (x1,y1 x2,y2)Integer x1 = math.abs(Integer.valueof(diffLines[i].substringBefore(',').trim()));Integer y1 = math.abs(Integer.valueOf(diffLines[i].substringBetween(',','+').trim()));Integer x2 = math.abs(Integer.valueOf(diffLines[i].substringBetween('+',',').trim()));Integer y2 = math.abs(Integer.valueOf(diffLines[i].substringAfter('+').substringBetween(',','@').trim()));String linesTobeAdded = diffLines[i].substringafter('\\'+'n+');lstWrapper.add(new wrapperDiffUnit(x1,y1,x2,y2,linesTobeAdded));}return lstWrapper;}/*** Method to remove all the lines that is either updated from the* original field(old value is no longer needed) or removed completely from the field** @author :Himanshoo Seth* @Since :06/03/2019* @param :List of wrapper diff unit which contains the extracted values of all* the coordinates and the lines to be updated in the originals lines* @param :Original Message splitted into list of lines on which the changes are to be applied* @return :void**/private void processSubtraction(List<wrapperDiffUnit> lstWrapper, List<String> originalLines) {Integer subVariable=0;for(wrapperDiffUnit unit : lstWrapper) {Integer xRefOne = unit.xRefOne;Integer yRefOne = unit.yRefOne;for(Integer j =0; j<yRefOne; j++){originalLines.remove(xRefOne-1-subVariable);}subVariable = subVariable + yRefOne;}}/*** Method to add all the lines that is either updated from the* original field(new value will be substituted) or newly added to the field** @author :Himanshoo Seth* @Since :06/03/2019* @param :List of wrapper diff unit which contains the extracted values of all* the coordinates and the lines to be updated in the originals lines* @param :Original Message splitted into list of lines on which the changes are to be applied* @return :void**/private void processAddition(List<wrapperDiffUnit> lstWrapper, List<String> originalLines) {for(wrapperDiffUnit unit : lstWrapper) {Integer xRefTwo = unit.xRefTwo;Integer yRefTwo = unit.yRefTwo;if(yRefTwo != 0){List<String> linesTobeAdded = unit.relatedString.substringafter('\\'+'n+').split('\\\\'+'n'+'\\+');for(Integer i=0; i<yRefTwo; i++){if((xRefTwo + i) < originalLines.size())originalLines.add(xRefTwo + i, linesTobeAdded[i]);else originalLines.add(linesTobeAdded[i]);}}}}/*** Method to get the reconstructed field value of a Long Text Area field from* the diff generated in CDC packet that contains the updates** @author :Himanshoo Seth* @Since :06/03/2019* @param :Original Message on which the changes are to be applied* @param :Diff Message which contains the updates* @return :The revised string variable that is the reconstructed* value from the diff that contains the updates.**/public String buildOriginalValueFromDiff(String originalMessage, String diffMessage) {// Split diff message into linesList<String> diffLines = toLines(diffMessage, diffSplitLineCharacter);// Split original text into lines.List<String> originalLines = toLines(originalMessage, newLine);// Get revised lines.List<String> revisedLines = generateRevisedLines(originalLines, diffLines);//combine the lines.String updateString = combineLines(revisedLines, newLine);// Generate SHA-256 hash on reconstructed value.string generatedHexval = encodingUtil.convertToHex(Crypto.generateDigest('SHA-256', Blob.valueOf(updateString)));// Extract hash from the event diff field.string hashCodeReceived = diffLines[0].substringafter('\\'+'n+++');// Compare extracted hash with generated hash and verify they are equal.if(!hashCodeReceived.trim().equals(generatedHexval)){//throw exception}return updateString;}// This is our wrapper/container class.public class wrapperDiffUnit {public Integer xRefOne {get; set;}public Integer yRefOne {get; set;}public Integer xRefTwo {get; set;}public Integer yRefTwo {get; set;}public String relatedString{get; set;}//This is the contructor method.public wrapperDiffUnit(Integer xRefOne, Integer yRefOne, Integer xRefTwo, Integer yRefTwo, String relatedString) {this.xRefOne = xRefOne;this.yRefOne = yRefOne;this.xRefTwo = xRefTwo;this.yRefTwo = yRefTwo;this.relatedString = relatedString;}}}
Comments
Post a Comment