Teach Me Salesforce

A community approach to learning salesforce.com

Archive for the ‘Uncategorized’ Category

Salesforce DX Monoliths: Chipping Away at the Metadata (part 2)

with 4 comments

After carefully removing all the metadata that I do not think I’ll need I got myself to the test of force:source:push again. It seems like I’ll always have to do –forceoverwrite because I always see a conflict with the Business Account record type.

The source:push generated some errors of course. The first two that jump out at me are those related to history-tracked fields:

force-app/main/default/objects/Lead.object-meta.xml The entity: Lead does not have history tracking enabled
N/A The entity: Account does not have history tracking enabled (12:13)

I mistook this for issues with the  true in the object files to accompany the true in the fields’ definitions but it actually appears to be due to true because when I deleted the Record Type History Tracking I was able to avoid the error.

The next error I got seems like it might actually be a bug (I’ll explain after)

N/A Required field is missing: businessProcess (75:18)

Since I’m trying to deploy Leads with a Record Type, you need to have a business process associated with the record type. I did keep the .businessProcess file for this purpose but I noticed that there is no  tag in the Lead’s .recordType file’s XML. I reverted the file back to its original state to make sure I hadn’t inadvertently deleted it. When I added  and the name of the Business Process, that error along with another one that was apparently related was cleared up.

force-app/main/default/permissionsets/My_pSet.permissionset-meta.xml In field: field – no CustomField named Lead.my_field__c found

I’m glad the above error was somehow dependent on fixing the business process because I had no idea why that would be considered missing. Now it seems like it must be due to the compiler’s inability to load the Record Type which defined that picklist’s values for the Record Type.

The next error was coming from the PersonAccounts, which frankly are performing much better than expected given their troublesome history.

force-app/main/default/objects/PersonAccount.object-meta.xml An unknown exception has occurred.

I was able to clear this up by removing  Private from the PersonAccount.object-meta.xml file. I didn’t consider it on my first review but when I was looking at the file now I realized that there is no sharingModel setting for a PersonAccount since it’s not a real object. Account takes care of its sharing model setting.

One error remains!

force-app/main/default/profiles/API.profile-meta.xml invalid cross reference id

The dreaded “invalid cross reference id”! Oh how many times I’ve been stumped by those 4 vague words. I guessed correctly that it’s probably the reference to the PersonAccount.PersonAccount record so I simply deleted it from the profile altogether…and Hooray! I got the metadata to push successfully!

Advertisements

Written by Always Thinkin

January 17, 2018 at 4:38 pm

Posted in Uncategorized

Building Salesforce DX Scratch Orgs from Monolithic Metadata

with one comment

Salesforce DX is revolutionizing SDLC for SFDC. Getting all your metadata from a mature org (the proverbial Monolithic metadataset) is a challenge. I am accepting that challenge and documenting the struggle here, for you.

This post will likely grow over time as I refine the process. Or, perhaps like the advice I’ve read to break down your metadata into distinct modular applications, this will break down into multiple posts.

My Salesforce environment is composed of ~35 UE Orgs that have similar metadata, roughly 80% consistency. So I’ll get to do this ~35 times to perfect it.

Our metadata is hopefully average enough that it represents a good cross-section of the typical Salesforce org out there and thus will help others in this project.

Some Metadata Stats:

Custom Objects: 44
Lines of Apex Code: 161,849 characters of Apex Code
Installed Packages: 0 (actually quite a few but left out of the DX metadata)

Step 1: Metadata Retrieval

I use Copado to backup my metadata daily to Github. So I did not have to use one of the metadata retrieval tools (e.g. Salesforce package.xml Builder) to establish my full set of metadata for conversion.

Step 2: Metadata Conversion

This was the first hurdle. I used sfdx force:mdapi:convert on the unedited metadata I retrieved and starting receiving errors. (error message TK)

I attempted to delete the specific referenced files like SvgIcon.cmp but it seemed like every file in the aura/SvgIcon/ and aura/Toast/ folders was going to throw the error so I deleted all the files and left the empty folders. This allowed me to successfully convert my metadata.

However, I made a mistake in the referenced target folder (-d OUTPUTDIR) in sfdx force:mdapi:convert -r . -d ./force-app and instead of going to the force-app/ folder I created as a sibling to config, the force-app folder was created automatically in the same folder as all my metadata.

Step 3. Create the Scratch Org

Nothing fancy here: sfdx force:org:create -d 30 -v 2U-DevHub -f config/project-scratch-def.json -a 2Ufull01 with the following configs:

{
“orgName”: “2U”,
“country”: “US”,
“edition”: “Enterprise”,
“hasSampleData”: “false”,
“features”: [“PersonAccounts”, “Communities”, “CustomApps”, “CustomTabs”, “DebugApex”, “API”,”MultiCurrency”, “AuthorApex”],
“orgPreferences”: {
“enabled”: [“NetworksEnabled”,”S1DesktopEnabled”, “ChatterEnabled”],
“disabled”: [“IsNameSuffixEnabled”]
}
}

Step 4. Pushing the Metadata

This is where I expect things to get really hard, and where I’ll be documenting what had to be done to make it work.

Next Up: Salesforce DX Monoliths: Exploring 2GP (2nd Generation Packaging)

Written by Always Thinkin

January 4, 2018 at 11:43 am

Posted in Uncategorized

Apex Homework Assignment 1: Control Flow – If Statements and For Loops

leave a comment »

I ran a bi-weekly Apex Workshop for the NYC Force.com Developer User Group from April 2014 to March 2015 and developed practice code to accompany the topics we covered. The focus was on helping Admins practice their Apex skills so they can become Developers. And now I’m sharing it with everyone so you can practice the skills you’ve learned in Trailhead or in classes or from reading documentation!

All the code is designed to work in any Developer Edition org without creating any new records or metadata. So you can just cut and paste this right into a new Apex Class to get started. Once you’ve copied it and saved it, start reading through the inline comments which provides some helpful notes about common Apex code conventions (and patterns you can re-use). Look for the “FIX ME!” comments which prompt you to uncomment certain lines and edit them so that the code compiles (saves) and that running the Unit Test will succeed.

This assignment depends a lot on looking at the Debug Logs to see the results from System.debug() statements – an important exercise in familiarizing yourself with the Logs!

And if you want to learn more about the Control Flow in this assignment, check out:
Cooking With Code: Conditional Statements in Apex (and Java)
Cooking with Code: Oh … for the Love of FOR Loops
at womencodeheroes.com!

 

/* This Apex Class is a Unit Test Class and all the code can be checked by running the test.
* Copy this into a new Apex Class named "ControlFlow" and save it.
* As you read through it, look for "FIX ME!" and make the changes suggested.
* If your changes are correct all the tests (System Asserts) will pass.
* 
* You can also see what's happening in the Logs by opening a Log and checking the Debug Only box
* you can compare the line numbers in the Log to the line number in the Class to find a specific
* debug entry.
*/

@isTest
public class ControlFlow {
    static testMethod void logic(){
        //prep a Map first so we have something to work with.
        Map<String, Decimal> metricMap = new Map<String, Decimal>{'Kilo' => 1000, 'Milli' => .001};
            
            //IF statements. These are just like Workflow Rules.
            if(true){
                //And here is your Workflow Action in between the curly brackets
                System.debug('Do Something!');
            }
        
        //you can condense it to one line if there's only one action.
        if(true) System.debug('Still do something');
        
        //prep a true/false variable for the next step.
        Boolean myBool = true;
        
        //use "else" when you only want the action to happen when your if case is false
        //This is what Workflow Rules can't do: actions for multiple results from one rule
        if(myBool){
            System.debug('The Boolean was true');//only runs if myBool is true
        } else {
            System.debug('The Boolean was false');//only runs if myBool is false
        }
        System.debug('The Boolean doesn\'t matter here');//This line will always run
        
        //you can string multiple condition cases with "else if"
        if(myBool){
            System.debug('The Boolean was true');
        } else if(!myBool){
            System.debug('The Boolean was false');
        } else {
            System.debug('Whoa! there was no value');
        }
        
        //FIX ME!: Try changing myBool to false and confirm that the conditions cascade to the next condition
        //FIX ME!: Try not setting myBool to any value and see what happens...
        
        //And just so you know it exists, there is the ternary operator for if-else statements
        //This has to be used with variable assignment, so it's use is limited but elegant
        //Here, we set a String of "true" or "false" depending on the Boolean value
        String theAnswer = (myBool ? 'true' : 'false');
        
        //FIX ME!: Google "Apex Boolean Methods" to find how to convert a String to a Boolean and then assert that myBool equals theAnswer
        //Since we cannot directly compare a String to a Boolean, you have to create a new Boolean "b" with the conversion.
        //Boolean b = ???;
        //System.assertEquals(myBool, b);        
        
        //Most of the time we're comparing two things in our conditions
        //You have to get used to typing "==" when you think "equals" in conditions
        if(metricMap.size() == 2){
            System.debug('Let\'s Add More!');
        }
        
        //FIX ME!: Now that you know about using "==" to compare values, 
        //rewrite this conditional block to accomodate null values.
        
        String myPicklist = 'true';
        //if(myPicklist){
        //System.debug('The value is true');
        //} else if(myPicklist){
        //System.debug('The value is false');
        //} else if(myPicklist){
        //System.debug('The picklist was empty');
        //} else {
        //System.debug('The value was neither true nor false');//what do you have to set myPicklist to to get this result? 
        //}
        
        //Any method that returns a boolean can be used in the condition clause.
        //With Maps, a common pattern is to check for a value's existence,
        //and if it's not there, put it in the Map.        
        if(metricMap.containsKey('Mega')){
            System.debug('Mega is already there!');
        } else {
            metricMap.put('Mega', 1000000);
        }
        System.assertEquals(metricMap.get('Mega'), 1000000);
        
        //FIX ME!: Add "Micro" to the metricMap so the following System Assert passes
        if(metricMap.containsKey('Micro')){
            //System.debug('Micro is already there!');
        } else {
            //metricMap.put('', .000001);
        }
        //System.assertEquals(metricMap.get(''), );
        
        //let's prep some Booleans to play with comparison operators
        Boolean x = true;
        Boolean y = false;
        
        //Conditionals can contain chains of variables (or boolean methods).
        //Comparison operators follow the rules of logic you use everyday in Reports, Views and Formulas
        if(x && y) System.debug('True AND False evaluates to False');
        if(x && !y) System.debug('True AND NOT False evaluates to True');
        if(x || y) System.debug('True OR False evaluates to True');
        if(!x || y) System.debug('Not True OR False evaluates to False');
        if(!(x && y)) System.debug('True AND False evaluates to False, BUT we re-evaluate that cumulative False to True with NOT(!)');
        
        //FIX ME! Add in a third Boolean "z" to get these conditions to be true
        Boolean z = true;
        //if(x && y) System.debug('Now it evaluates to True!');
        //if(!x || y) System.debug('Now it evaluates to True!');
        
        
        
        ////////////////
        // FOR LOOPS  //
        ////////////////
        
        //For Loops allow you to work with multiple variables or objects
        //There wasn't a good equivalent in the UI until Process Builder came along
        //In Apex, they are an essential control for, well, everything!
        
        //First variation: For each item in a List or Set, do something
        Set<String> metricSet = metricMap.keySet();//Remember these are: ['Mega', 'Micro', 'Milli']
        
        for(String s : metricSet){//for every string in metricSet do something...
            System.debug(s);
        }
        
        //FIX ME! Now that we've looped through the keys in metricSet, loop through the values
        List<Decimal> metricValues = metricMap.values();
        //for(){
            //system.debug();
        //}
        
        //Second variation: for each record returned by a SOQL query, do something.
        for(User u : [Select Name from User]){
            System.debug(u.Name);
        }
        
        //FIX ME! Write a for loop that retreive all the Profile Names and prints them in a System Debug
        //Note that in Unit Tests without SeeAllData = True, Users and Profiles are still accessible along with a few other special Objects
        
        //for(Profile p : []){
        	//System.debug();
        //}
        
        
        //Third variation: repeat the loop as many times as instructed
        //This uses a special format common to many languages of establishing:
        //1. An Entry Condition: Integer i = 0 (arbitrary, but 0 is useful when working with list indices)
        //2. A Comparison: i < 5 (means "as long as")
        //3. An Incrementor: i++ (add 1 to i each loop)
        for(Integer i = 0 ; i < 5 ; i++){
            System.debug('i now equals: ' + i);
        }
        
        List<String> names = new List<String>{'Dave', 'Jim', 'Sara', 'George'};
            String d = names[0];
        
        //A very frequent pattern is repeating the loop for each item in a list using the list.size() method
        for(Integer i = 0; i < names.size(); i++){
            System.debug('Value at index ' + i + ' is ' + names[i]);//we can use i to retrieve each item in the list in order
        }
        
        //FIX ME! Make a for loop of 5 Integers that adds each Integer to itself
        //use a system debug to output the result like "1 + 1 = 2"
        //you have to use 3 variations on + to do this: 
        //1. + to concatentate two items
        //2. + as a string to print out the plus sign (and later the equals sign)
        //3. + as a mathematical operator to add the two Integers
        //for(){
            //System.debug();
        //}
        

            
        //Loop-di-Loop. You can embed loop within loops.
        
        //prep some units of measurement
        List<String> units = new List<String>{'meter', 'watt', 'gram'};
        //The first iteration will loop through all the values in the inner loop first
        //and then proceed on to the next item in the outer loop
        for(String u : units){
            for(String m : metricMap.keySet()){
               System.debug('A ' + m + u + ' is ' + metricMap.get(m) + u + 's');//We use m to get the string and the metric multiplier from the Map!
            }
        }
    }
}

Written by Always Thinkin

June 23, 2015 at 8:42 am

Posted in Uncategorized

Apex Homework Assignment 0: Collections – Lists, Sets and Maps

with 3 comments

I ran a bi-weekly Apex Workshop for the NYC Force.com Developer User Group from April 2014 to March 2015 and developed practice code to accompany the topics we covered. The focus was on helping Admins practice their Apex skills so they can become Developers. And now I’m sharing it with everyone so you can practice the skills you’ve learned in Trailhead or in classes or from reading documentation!

All the code is designed to work in any Developer Edition org without creating any new records or metadata. So you can just cut and paste this right into a new Apex Class to get started. Once you’ve copied it and saved it, start reading through the inline comments which provides some helpful notes about common Apex code conventions (and patterns you can re-use). Look for the “FIX ME!” comments which prompt you to uncomment certain lines and edit them so that the code compiles (saves) and that running the Unit Test will succeed.

And if you want to learn more about the Collections in this assignment, check out Cooking with Code: Amuse-bouche* of Apex Collections (Lists, Sets, and Maps) at womencodeheroes.com!

Good luck – and let me know what you think!

/* This Apex Class is a Unit Test Class and all the code can be checked by running the test.
* Copy this into a new Apex Class named "Collections" and save it.
* As you read through it, look for "FIX ME!" and make the changes suggested.
* If your changes are correct all the tests (System Asserts) will pass.
* 
* You can also see what's happening in the Logs by opening a Log and checking the Debug Only box
* you can compare the line numbers in the Log to the line number in the Class to find a specific
* debug entry.
*/

@isTest
private class Collections {
    static testMethod void Lists(){
        //LISTS ARE AN ORDERED COLLECTION OF DATA
        
        //How to create a List with some pre-defined values
        String someword = 'rope';
        List<String> threeStrings = new List<String>{'rope', 'twine', 'thread'};
            System.assertEquals(3, threeStrings.size());//.size() is an Apex List Method - Google & learn more!
        System.assertEquals('rope', threeStrings[0]);//List items start with Index 0, that's what [0] means
        
        //How to create a List with a controlled number of items
        String[] oneWord = new String[1];//here [1] means "room for only 1 item in the list"
        oneWord[0] = 'able';//here [0] means put the word in Index 0 (the first item in the list)
        //oneWord[1] = 'baker';//Won't Work! Try uncommenting an running the test. The List was configured for only 1 item
        oneWord.add('charlie');//Will Work! The .add() List Method automatically expands the List
        oneWord[1] = 'baker';
        System.debug(oneWord);//System.debug will show up in the Logs so you can check the value at any time
        System.assertEquals(2, oneWord.size());
        
        //How to create an empty List with no limit on items
        List<String> noStrings = new List<String>();
        System.assert(noStrings.isEmpty());
        noStrings.add('rock');
        System.assertEquals(1, noStrings.size());
        
        //FIX ME! Using the values in the first List we made, uncomment the two lines below and get them to pass
        System.assertEquals('rope', threeStrings[0]);
        //System.assertEquals('', threeStrings[1]);
        //System.assertEquals('', threeStrings[2]);
        
        //Lists can also be Lists of Lists. The insides lists don't have to have a name, you can reference them with the Index number
        List<List<String>> numberNames = new List<List<String>>();
        numberNames.add(new List<String>{'zero', 'one', 'two', 'three', 'four'});
        numberNames.add(new List<String>{'cero', 'uno', 'dos', 'tres'});
        numberNames.add(new List<String>{'ling', 'yi', 'er'});
        System.assertEquals(3, numberNames.size());
        System.assertEquals('four', numberNames[0][4]);
        System.assertEquals('tres', numberNames[1][3]);
        //FIX ME! Uncomment and add the correct value to the make it pass 
        //System.assertEquals('', numberNames[2][1]);
        
        //FIX ME! Using the data in numberNames uncomment the lines below and get them to pass
        //System.assertEquals(4, numberNames[1].size());
        //System.assertEquals(3, numberNames[2].size());
        
        //FIX ME! Uncomment and add the correct value to make it pass
        //System.assertEquals('', numberNames[1][1] + ' ' + numberNames[1][2] + ' ' + numberNames[1][3]);
        //You can use System.debug(numberNames[1][1] + ' ' + numberNames[1][2] + ' ' + numberNames[1][3]); to see what it is if you need help
        
    }
    
    static testMethod void Sets(){
        //SETS ARE AN UNORDERED COLLECTION OF UNIQUE DATA
        
        Set<String> planets = new Set<String>{'Mars', 'Earth', 'Pluto'};
            System.assertEquals(3, planets.size());
        
        Set<String> months = new Set<String>();
        months.add('June');
        months.add('July');
        months.add('June');//You can try to add this again, but it will have no effect
        System.assertEquals(2, months.size());
        System.assert(months.contains('July'));// contains() is a useful Set Method that returns true/false
        
        Set<Id> Ids = new Set<Id>();//IDs are very useful in sets. Adding the same idea twice to a List will fail, but not Sets!
        Id me = UserInfo.getUserId();
        Ids.add(me);
        System.assertEquals(1, Ids.size());
    }
    
    static testMethod void Maps(){
        //MAPS ARE NAMED COLLECTIONS OF DATA
        Map<String, String> fieldValues = new Map<String, String>{'Name' => '2U, Inc.', 'Website' => '2u.com', 'Phone' => '301-892-4350'};
            System.assertEquals(3, fieldValues.size());
        
        Map<Integer, String> numbers = new Map<Integer, String>();
        System.assert(numbers.isEmpty());
        numbers.put(1, 'One');
        numbers.put(2, 'Two');
        numbers.put(3, 'Three');
        System.assert(numbers.containsKey(1));
        
        System.assertEquals('One', numbers.get(1));
        
        //FIX ME! Using the data in the numbers map, uncomment the lines below and get them to pass
        //System.assertEquals('', numbers.get(2));
        //System.assertEquals('Three',);
        
        System.debug(numbers.keySet());//keySet() returns the Keys from your Map as a Set.
        //FIX ME! Populate the Set called 'myKeys' with Integers to get the assert to pass
        //Set<Integer> myKeys = ;
        //System.assertEquals(numbers.keySet(),myKeys);
        
        System.debug(numbers.values());//values() returns the Values from your Map as a List.
        //FIX ME! Populate a List called 'myValues' that you can use to get the assert to pass
        //List<String> myValues = ;
        //System.assertEquals(numbers.values(), myValues);
        
        //FIX ME! On the Map Methods page in the Apex docs, find the method that allows you to remove an item from the Map
        // and get the assertion to pass
        //numbers.??????(2);
        //System.assertEquals(2, numbers.size());
        
        //FIX ME! And now find and use the method to remove all the values from the Map
        // and get the assertion to pass
        //numbers.?????();
        //System.assert(numbers.isEmpty());
    }
    
    @isTest(SeeAllData=True)
    static void SOQLandCollections(){
        //This only works when this test Class is set to SeeAllData=True which is an Apex Annotation that exposes real data in unit tests. 
        List<Account> accts = [Select ID from Account];
        System.debug(accts.size());
        System.debug(accts);
        //System.debug(accts[0].Name);//This won't work because Name was not acquired in the query.
        
        accts = [Select ID, Name from Account];
        System.debug(accts.size());
        System.debug(accts);
        System.debug(accts[0].Name);//Now it works because we included Name in our query.
        
        //Populating a Map from a SOQL Query is also easy, although not necessarily intutive
        //Note that you have to "cast" the query results (a List) to a Map
        Map<Id, Account> acctMap1 = new Map<Id, Account>([Select ID from Account]);
        System.debug(acctMap1);
        
        
        
        //COMMON ROOKIE ERROR
        //Using Lists to update record instead of Maps
        List<Account> acctList = [Select ID, Name, Industry, Ownership from Account];
        List<Account> acctListToUpdate = new List<Account>();
        
        //{There might be some logic here that determines Type must be updated on a record}
        acctList[0].Type = 'Retailing';
        acctListToUpdate.add(acctList[0]);
        
        //{There might be some other logic here that determines Rating must be updated on the same record}
        acctList[0].Rating = 'Super Hot';
        acctListToUpdate.add(acctList[0]);
        
        System.debug(acctList[0]);
        
        System.debug(acctListToUpdate);
        /* The update below compiles: you can save this class with it uncommented, but the class
		* fails when we run the test because we added the same record to the list twice.
		* Uncomment it and see what happens. Then comment it out again and move on to the better solution.
		*/
        //update acctListToUpdate;//Fails with "Duplicate id in list" exception.
        
        
        //Let's try the same thing, but using a Map to hold the records to be updated
        Map<ID, Account> acctMapToUpdate = new Map<Id, Account>();
        
        //We make the same field/values assigment as before on the same record
        acctList[0].Type = 'Retailing';
        //but now we put the record in a Map instead of a list
        acctMapToUpdate.put(acctList[0].Id, acctList[0]);
        
        //And we do it again for the Rating Field
        acctList[0].Rating = 'Super Hot';
        //when we put it a second time, the Map recognizes that the ID already exists, and just 
        //amends the data in the Map with the new information 
        acctMapToUpdate.put(acctList[0].Id, acctList[0]);
        
        system.debug(acctMapToUpdate);
        update acctMapToUpdate.values();
        
        //Here's an unexpected pattern for SOQL queries with Parent & Child records
        //You would expect that it might be a List<List> pattern so you could call Contacts
        //with acctsCons[0][0] but instead you must use dot notation to reference the sub-lists
        List<Account> acctCons = [SELECT Name, (SELECT FirstName, LastName FROM Contacts) FROM Account];
        system.debug(acctCons[0].Contacts[0].FirstName);
        
    }
    
}

Written by Always Thinkin

June 21, 2015 at 11:16 am

Posted in Uncategorized

Apex – Sorting a map

leave a comment »

So this is going to be one of the more apex heavy posts. This is a challenge I think many developers
have come across, and while what I propose is by no means the most elegant thing ever, it does do the
job until hopefully salesforce implements a native map sorting method. So here is the basic approach

1) populate map with information
2) create another map with the keys as the values you want to sort by, and the value as the key
for the other map.
3) create a list whos values are the keyset of the map created in step 2
4) sort the list
5) iterate over the list which now has your values in order, get the value from map 2 using the
current loop iterator as the key.

Sounds pretty complicated eh? It’s not SO bad once you kinda get the hang of it, but there is one
gotcha that kind of sucks, but we’ll cover that in a minute.

Here is a sample using a simple object called contestEntry. We’ll create a bunch of them with
random ordering, and then sort them and loop over the sorted result. You should be able to run this
in any org so you can see the principals in action.

        public class contestEntry
        {
            public decimal rank{get;set;}
            public string name{get;set;}
        }

        map&lt;string,contestEntry&gt; entries = new map&lt;string,contestEntry&gt;();

        contestEntry entry1 = new contestEntry();
        entry1.rank = 5;
        entry1.name = 'Frank';
        entries.put(entry1.name,entry1);

        contestEntry entry2 = new contestEntry();
        entry2.rank = 3;
        entry2.name = 'Bob';
        entries.put(entry2.name,entry2);

        contestEntry entry3 = new contestEntry();
        entry3.rank = 1;
        entry3.name = 'Jones';
        entries.put(entry3.name,entry3);

        contestEntry entry4 = new contestEntry();
        entry4.rank = 4;
        entry4.name = 'Sandy';
        entries.put(entry4.name,entry4);

        contestEntry entry5 = new contestEntry();
        entry5.rank = 2;
        entry5.name = 'Felix';
        entries.put(entry5.name,entry5);

        //oh no, these entries are all out of order.
        system.debug(entries) ;

        //lets get sorting these guys. First we'll need a map to store the rank, and the contestEntry that rank is
        //associated with
        map&lt;decimal,string&gt; rankToNameMap = new map&lt;decimal,string&gt;();
        for(contestEntry entry : entries.values())
        {
            rankToNameMap.put(entry.rank,entry.name);
        }
        //now lets put those ranks in a list
        list&lt;decimal&gt; ranksList = new list&lt;decimal&gt;();
        ranksList.addAll(rankToNameMap.keySet());

        //now sort them
        ranksList.sort();

        //ok, so now we have the ranks in order, we need to figure out who had that rank
        for(decimal rank : ranksList)
        {
            String thisEntryName = rankToNameMap.get(rank);
            contestEntry thisEntry = entries.get(thisEntryName);
            system.debug(thisEntry);
        }

Walking through it first we just make a sample object to use here. Normally this would be whatever you are actually trying to sort, but for the sake of easyness I just created an object called contestEntry. It just holds a name and a rank.

So then I make a map of those things, keyed by the persona name, and containing the contestEntry object. You might in real life have a map of sObjects keyed by their Id and containing the sObject itself. So then I make a bunch of those and add them to the map in random order so my sorting actually has some work to do 😛

The next thing is creating a map with the key of the value I want to sort this list by. The value is key of the first map. So in real life this might be a dollar amount on an opportunity and then the opportunity Id if the original map was a list of opportunities keyed by their Id.

We loop over all the objects in the original map, and add them to our temporary sorting map, again keyed by value to sort by, and value with key from original map.

Then we create a list of the type of the key of the temporary sorting map. Your key was a decimal? Then your list is of decimals as well, etc. Then add all the keys from the sorting map to the list you just made.

Sort the list.

Iterate over the sorted list, each entry in this list will be a key you can use to get the entry from the sorting map, which will contain the key to the original map. You now have a reference to your original map value by whatever value you sorted on.

There is however one gotcha with this approach. If you have duplicates in the value you are going to sort by, you are going to end up with collisions in your sorting map, which will end up with values overwriting each other and ultimately values missing in your final iteration. This happens because say in my example I have two people with rank 1. First person comes through, their rank is 1, and their name is Sandy. So the sorting map has 1=sandy. Then another person comes through, they also have rank 1 and their name is jones. Now the map has 1=jones. Sandy just fell out of the list. How do you deal with this? The best hack-ish fix I could come up with is to see if the key you are attempting to write to already exists, if so, then write to a slightly higher value key. Basically replace

       for(contestEntry entry : entries.values())
        {
            rankToNameMap.put(entry.rank,entry.name);
        }

with

for(contestEntry entry : entries.values())
        {
                decimal rank = entry.rank;
                while(rankToNameMap.containsKey(rank))
                {
                    system.debug('------ INCRIMENTING Rank TOTAL FOR ' + entry.name);
                    rank += 0.001;
                }

                rankToNameMap.put(rank,entry.name);
        }

This won’t affect the value displayed when you retrieve and display the object, it just changes the sort order. I haven’t tested this throughly though so don’t rely on it extensively.

Anyway, there you have it Apex fans. A method to reliable (if not quickly or efficiently) sort Apex maps by just about anything. Once you understand the concepts it’s fairly easy to expand to sort to your hearts desire.
Originally posted by Kenji776 @ http://iwritecrappycode.wordpress.com/2011/09/28/apex-sorting-a-map/

Written by kenji776

September 28, 2011 at 5:07 pm

How To Write A Trigger Test

with 3 comments

One of the most often asked questions is “how do I test a trigger I have written”. This normally stems from the misunderstanding of how triggers run and what they actually do. A trigger is a piece of code that is defined to run either “before” or “after” a particular action is performed upon a record, namely “insert”, “update”, “undelete” or “delete”.

In the definition of your trigger you will know what context the trigger is occurring in (for example “after insert”) and you will have an understanding of the functionality this trigger will be performing. To test a trigger you must create an apex class which acts upon the record in the desired way (e.g. inserting a record) so that the trigger code will then get executed.

The example trigger shown below is a before trigger on a test object we have called TestObject__c with two text fields (Field1__c and Field2__c) and a Name field. When a record of this object type is sent to be inserted, before the insert occurs, our trigger runs and checks to see whether or not Field2__c is null (i.e. has been set) and if it has been set it then sets Field1__c to have this same value (trivial yes, but hopefully illustrative).


trigger TestObjectTrigger on TestObject__c (before insert)
{
for(TestObject__c obj : trigger.new)
{
obj.Field1__c = obj.Field2__c == null ? obj.Field2__c : null;
}
}

So we know what this trigger is doing, but what do we have to do to test it? At the highest level we want to insert a record and check that the trigger behaves as expected. So if we set a value for Field2__c then Field1__c will also get that value. What if we don’t set a value? Then we expect Field1__c to be null, so we need to test for that as well. Finally, when testing triggers we should attempt to do some volume testing as triggers are where we can easiest hit governor limits through looping etc (I know the limits are now increased, but good practice is a still good practice). So we will also include a test to insert 100 records and check they act as expected.

You will see in the code below that I have three private static methods in my test class to manage the insertion of the records. I recommend doing it this way for a number of reasons:

  1. You will have cleaner and better structured code
  2. You will reduce the number of places where bugs can be
  3. Your tests will be more consistent and easier to update and maintain
  4. You can easily add extra tests
  5. You can create very exact specifications for your tests easily and also remove a lot of data dependency problems

What do I mean by removing data dependency problems? If you are writing a trigger on a standard SObject (e.g. Account) or for a product that will be upgraded, then your tests will run against orgs where data of that type exists. Having very clear and structured data setup methods allows you to minimise the chance of someone already having records like those you are going to be considering (you will notice I use PBTest to help keep mine unique as it is an unlikely name to be given to an object and so I am unlikely to mistakenly retrieve the wrong object).

@isTest
private class TestObjectTriggerTest
{

static TestMethod void Test0_TestInsertWithValue()
{
string test0Value = ‘PBTest 0’;
insertTestObjectWithField2Value(test0Value);

TestObject__c testObj = [Select Name, Field1__c, Field2__c from TestObject__c where Name = :test0Value];

System.assertNotEquals(testObj, null, ‘Test 0 object was null and not inserted correctly’);

System.assertEquals(testObj.Field1__c, testObj.Field2__c, ‘Field1 and Field2 not equals in test 0’);
}

static TestMethod void Test1_TestInsertWithoutValue()
{
insertTestObject();

TestObject__c testObj = [Select Name, Field1__c, Field2__c from TestObject__c where Name = ‘PBTestValue’];

System.assertNotEquals(testObj, null, ‘Test 1 object was null and not inserted correctly’);

System.assertEquals(testObj.Field1__c, testObj.Field2__c, ‘Field1 and Field2 not equals in test 0’);
}

static TestMethod void Test2_TestMultipleValues()
{
List values = new List();
for(integer i=0; i<100; i++)
{
values.add(‘PBTest ‘ + i);
}

insertTestObjectsWithField2Values(values);

List objects = [Select Name, Field1__c, Field2__c from TestObject__c where Name in :values Order By Name];

for(TestObject__c obj : objects)
{
System.assertEquals(obj.Field1__c, obj.Field2__c, ‘In test 3, test object ‘ + obj.Name + ‘ Field 1 and Field 2 differ’);
}
}

private static void insertTestObject()
{
TestObject__c obj = new TestObject__c();
obj.Name = ‘PBTestValue’;
insert obj;
}

private static void insertTestObjectWithField2Value(String field2Value)
{
TestObject__c obj = new TestObject__c();
obj.Name = field2Value;
obj.Field2__c = field2Value;
insert obj;
}

private static void insertTestObjectsWithField2Values(List fieldValues)
{
integer i = 0;
List objs = new List();
for(string value : fieldValues)
{
TestObject__c obj = new TestObject__c();
obj.Field2__c = value;
obj.Name = ‘PBTest’ + i;
objs.add(obj);
i++;
}

insert objs;
}
}

The code is self-explanatory and should give enough insight as to how a trigger is tested properly. It runs and passes at 100%.

A final note is that trigger testing is extremely important, not only because Salesforce mandate it but because trigger code is some of the most used code you have in your system. As a system grows and its rules become more complex with multiple records being created automatically of the back of the creation of other records, your trigger code will be used extensively and therefore thorough testing (including volumes) is a must.

Written by pbattisson

May 7, 2011 at 7:17 am

Let’s Teach People Salesforce.com!!

leave a comment »

Genius Idea!Wes Nolte and I recently wrote the Salesforce Handbook. The book is doing well and people seem to really like it. However, it’s still a printed book with static content that must be updated to stay relevant (#fail). During the writing process Wes and I talked about a living, breathing version of the book to continue to teach, demonstrate best practices and provide sample code and configuration tips. However, we have families to feed and writing isn’t that profitable, let alone blogging.

I was flying home for San Francisco last week and was browsing the salesforce.com message boards (i.e., I have no life). There are still a ton of people asking basic questions like, “how do I test a trigger” or “how do I set a default value in my Visualforce page” or “why is this validation rule doing this ….”. Some of these questions have already been answered but they are scattered around a number of community blogs. Wouldn’t it be great if there was a centralized location to teach people salesforce.com? [insert magic light bulb ding sound]

So during my red-eye-flight-stupor, I decided to start this blog to help people learn saleforce.com. I’m inviting everyone in the salesforce.com community to contribute. If you have a workflow tip, some sample Apex code or an explanation on territority management that you’d like to share, I’ll add you as a contributor and you can post it. Perhaps you saw a great recipe on the Force.com Cookbook site. Post a link to it. You found a great Ruby book on Amazon.com. Post a link to it. A cool demo on YouTube. Post a link to it. Get the idea?

Can you post content that already exist on your blog? Not a problem as long as the goal is to educate the salesforce.com masses. Feel free to cross-post away! The more the merrier. I’ve put together a few simple guidelines to help us focus and provide value.

If you would like to contribute, simply send me an email at jeff[at]jeffdouglas(dot)com with your wordpress.com email address and I’ll add you. If you don’t already have a wordpress.com account you must create one first.

My hope is that people will be interested in this site and not only read it but provide content. We’ll see. Let the crowdsourcing learning experiment begin!

Written by Jeff Douglas

April 29, 2011 at 3:34 pm

Posted in Uncategorized