Teach Me Salesforce

A community approach to learning salesforce.com

Trailhead Review: Apex Specialist Superbadge

leave a comment »

This article is about my first Trailhead Super Badge, the Apex Specialist.

Challenge 1
Challenge 1 requires a fair amount of declarative admin work to get the objects and components in place to allow you to test in the UI (you could probably write the code without it but it’s important to test in the UI and in code).

Because Trailhead playgrounds use the base Developer Edition architecture, there are also all the usual custom fields and data, so it’s helpful to be familiar with that so that you can easily distinguish the components added by the managed package and those that are in every DE org (like SLAViolation__c).

I predict that the creation of Products in the unit tests could be annoying if it involves adding them to an active Price Book :/

The instructions are appropriately vague, like real-world requirements, and do require you to read them closely to understand the goals of the challenge. I’ll confess that I don’t usually perform Test-Driven Development, but for this Superbadge, I am doing that because it’s way easier to test bulkification with unit tests.

One aspect that I didn’t expect was that I could complete this challenge without implementing everything specified in the Business Requirements for Automate Maintenance Requests. I had intended to include all of those requirements to be thorough and ensure that I passed on the first attempt but for the benefit of you the Reader, I thought I’d see what happens if I checked the challenge before completing everything. I won’t say what I left out because I think that part of the learning process is how to read and execute requirements. And I assume it will be checked in a later challenge…I’ll let you know.

Time so far: 2 hours

Challenge 2
Challenge 2 jumps into callouts, nothing very difficult since the external service is already set up for you, so you just have to retrieve the data and get it added into your Trailhead playground. It requires some of the lessons learned in the Apex Integration Services module to complete.

And I was still able to pass the challenge without completing all the Business Requirements for Automate Maintenance Requests.

Time to complete: 1 hour

Challenge 3
Challenge 3 is the simplest of the challenges so far, although I tried to make it more difficult expecting that I had to complete the requirement entirely in code rather than utilizing some declarative configuration.

Still no check on all the requirements in Automate Maintenance Requests. Maybe they’ll come up in the unit tests.

Time to complete: 30 minutes

Challenge 4
This is the start of the unit test challenges and despite the instructions to create positive and negative tests, the check appears to only care about 100% coverage which I had achieved during my test-driven development in Challenge 1 without negative tests. I did bulkify my original tests because I always do but I wonder if it even checks for that.

It never did check to see whether I completed all the Business Requirements in Automate Maintenance Requests.

Time to complete: 0 minutes (because I built the unit tests during Challenge 1).

Written by Always Thinkin

December 29, 2018 at 1:46 pm

Posted in Apex, Trailhead

Trailhead Review: Quick Start: Apex Coding for Admins

leave a comment »

Review of Quick Start: Apex Coding for Admins

This Project won’t teach you a lot of Apex concepts, but is good practice creating Apex Classes and Triggers in the Setup UI and using Workbench with Anonymous Apex to execute the code. The Steps themselves don’t try to teach you Apex and just involve cutting and pasting code then executing the code, however the comments are thorough, one for each line. The comments use formal terminology to describe each line’s action, so they are a good lesson in speaking “Developer”.

Overall, the instructions could add more detailed conceptual descriptions to help Admins grasp what they are doing, perhaps even comparing the actions to what they might already know from declarative development (e.g. the BankAcct class is similar to a Custom Object with number and text fields, plus a Workflow Field Update that adds numbers).

Step 1 is just about setting up the metadata for this project.

Step 2 is just copying and pasting to create two Apex Classes.

Step 3 is just another copy and paste into Workbench to verify that the previous classes behave as expected.

Step 4 introduces Lists and replaces the code fromStep 2 to include a List.

So far, not many concepts have been discussed in the Trailhead itself, but the comments in the code are thorough and should be read carefully to get the most out of this module.

Step 5 introduces For Loops (although without really explaining what they’re for) and updates an Apex class to include a For Loop.

Step 6 introduces DML, specifically the database insert method to add Contacts.

Step 7 introduces SOQL and updates an Apex class to include a SOQL query.

Step 8 adds a Trigger and tests it by creating a custom object record to verify that the Contact insert from Step 6 works.


Written by Always Thinkin

December 9, 2018 at 4:49 pm

Posted in Apex, Beginner

Tagged with

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!

Written by Always Thinkin

January 17, 2018 at 4:38 pm

Posted in Uncategorized

Salesforce DX Monoliths: Chipping Away at the Metadata

leave a comment »

Yesterday I started preparations to build a package from actual production metadata after experimenting with 2GP in a Trial DX Org first. My goal is quite modest: a package with the 4-5 fields and matching Permission Set necessary for one of our integrations.

The preparations followed a predictable pattern:

  1. Authorize a new DevHub (I’ve got 35 orgs, I hope my DevHub aliasing convention works for the other 32 yet to be DXed!)
  2. Clone the git repo where our metadata is backed up daily by Copado (fast easy way to do this).
    1. Create a new branch for DX-related manipulations.
  3. Create a new DX project (finally understood the difference between –outputdir and –defaultpackagedir. I mistook outputdir as the location that new metadata files would go into eventually. But it’s simply the location that the sfdx-project.json, config/scratch-project-def.json, .forceignore and the readme.MD)

Then began the process of getting just the metadata I wanted in the package. I saw a couple approaches:

  1. Create and populate the files manually (maybe just cutting and pasting from actual metadata).
  2. Retrieve the actual metadata and remove what I don’t want in the package.

The first would probably have been faster and what I would do in the future but I thought it would be a good exercise to remind myself of all the metadata that is there and be sure I kept all the dependencies intact.

So for a couple hours I used git rm -r to remove all the unnecessary metadata from the branch. This was tedious, especially when I had to leave one file like a Permission Set but delete all the other files in a folder.

After getting down to just the two Objects with fields I want plus the Profile and Permission Set that will be used for the integration user, I attempted my first $sfdx force:mdapi:convert -r ../src/ -d force-app/
and I got an errorI had deleted my package.xml and had to restore it and strip out all the unnecessary references.

After I restored the package.xml, the convert worked although I got an apparently non-critical fatalError message because I left a ” </recordTypeVisibilities>” tag in the profile file. I think I also had a missing end tag in the package.xml.

[xmldom fatalError] end tag name: recordTypeVisibilities is not match the current start tagName:Profile
[xmldom error] element parse error: end tag name: recordTypeVisibilities is not match the current start tagName:Profile
[xmldom fatalError] end tag name: Profile is not match the current start tagName:undefined
[xmldom error] element parse error: end tag name: Profile is not match the current start tagName:undefined

But the convert worked anyway. I had decided to leave the two Object files intact rather than remove all the additional metadata in them. So I ended up with extra folders I’ll have to delete:

  • fieldSets
  • listViews
  • recordTypes
  • validationRules
  • webLinks
  • Plus the Account.object-meta.xml with ActionOverrides and other metadata I won’t need.

When I ran sfdx force:source:push -f I left <trackHistory>true</trackHistory> in the field definition files but had deleted the object-metadata.xml file that contained the <enableHistory>true</enableHistory> so I got errors like:

N/A                      The entity: Account does not have history tracking enabled (3:13)
N/A                      The entity: Lead does not have history tracking enabled (3:13)

And I had a couple of custom picklist values in the Lead businessProcess and the Account recordType files that I had to remove from the XML.

───────────────────────────────────────────────────────────N/A                      Picklist value: New not found (48:24)
N/A                      Picklist value: inboundcounselor in picklist: AccountSource not found (37:18)

But, one of those picklist values was the default, so I got a very tricky error:

───────────────────────────────────────────────────────────N/A                     No default value specified (48:24)

And I could only figure that one out by noticing that the 48:24 reference matched a previous error so at least I could look in the correct file for the problem.

At this point I recognized that I should have left the StandardValueSet in place so that the businessProcess file can refer to the picklist values for Lead.Status. I was expecting that other dependencies were waiting for me so I decided I should just start over. git reset –hard ffc1b. Glad I was committing frequently as I made small changes!


Written by Always Thinkin

January 14, 2018 at 5:16 pm

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

with one comment

In preparation for breaking up my monolithic metadata, I’m working out how to use Second Generation Packaging or 2GP. Following the documentation’s suggestion, I’m not doing this in my own production orgs but rather in a DX Trial Org.

I studied first to get an idea of how to use 2GP, you can see the Resources list of those sites below.

I created a Scratch Org and added only 2 simple components: a custom field and a permission set for that field.

When I attempted to pull the components with force:pull to my local environment I got the error:

ERROR: Metadata API received improper input. Please ensure file name and capitalization is correct. Load of metadata from db failed for metadata of type:Profile and file name:admin.

which I could only resolve by adding Admin.profile to my .forceignore file. At first this bothered me because I was concerned that the Admin profile would not get access to the new field but as with any installed package, you can ensure that Admins get access to all components during the install process.

After getting the metadata pulled down to my /force-app folder I started the process of creating an unlocked package.

I started by running sfdx force:package2:version:create -d force-app -v gs-trial-devhub and got the expected return message:

Package2 version creation request is InProgress. Run “sfdx force:package2:version:create:get -i 08c1I000000003jQAA” to query for status.

but when I ran sfdx force:package2:version:create:get -i 08c1I000000003jQAA I got an unexpected message:

Tag, Branch, CreatedDate FROM Package2VersionCreateRequest WHERE
ERROR at Row:1:Column:125
sObject type ‘Package2VersionCreateRequest’ is not supported. If you are attempting to use a custom object, be sure to append the ‘__c’ after the entity name. Please reference your WSDL or the describe call for the appropriate names.

Try this:
Second-generation packaging is not enabled on this org. Verify that you are authenticated to the desired org and try again. Otherwise, contact Salesforce Customer Support for more information.

And, yes, I had neglected to enable Second Generation Packaging in my org. That was easy enough to resolve. After enabling it I got the expected return messages when running sfdx force:package2:version:create:get -i 08c1I000000003jQAA

=== Package2 Version Create Request
────────────────────────────── ──────────────────
ID 08c1I000000003jQAA
Status InProgress
Package2 Id 0Ho1I0000008OJDSA2
Package2 Version Id
Subscriber Package2 Version Id
Created Date 2018-01-09 18:09

And a little while later, success!

=== Package2 Version Create Request
────────────────────────────── ──────────────────
ID 08c1I000000003jQAA
Status Success
Package2 Id 0Ho1I0000008OJDSA2
Package2 Version Id 05i1I000000001nQAA
Subscriber Package2 Version Id 04t1I000003cm03QAA
Created Date 2018-01-09 18:09

After that though, things got troublesome. I decided to see what happens if you try to install a package that has not been released with the command so I ran sfdx force:package:install -u gs-trial-devhub -i 04t1I000003cm03QAA and I got:

PackageInstallRequest is still InProgress or Unknown. You can query the status using
sfdx force:package:install:get -i 0Hf1I000000Qe9cSAC -u gs-trial-devhub

When I ran the suggested command, sfdx force:package:install:get -i 0Hf1I000000Qe9cSAC -u gs-trial-devhub, I got:

ERROR: Encountered errors installing the package!,Installation errors:
1) Package(0331I000000m3Qe) Unable to install beta package, Details: The package you attempted to install is a beta package, which you can only install in Force.com Sandbox or Developer Edition organizations.
ERROR: Installation errors:
1) Package(0331I000000m3Qe) Unable to install beta package, Details: The package you attempted to install is a beta package, which you can only install in Force.com Sandbox or Developer Edition organizations.

(Note that the above messages changed recently to read “Can’t install package in a production org, Details: You can install this second-generation package only in a sandbox or scratch org.”)

Now I assumed this was because I hadn’t released the package, so I ran sfdx force:package2:version:update -i 05i1I000000001nQAA -v gs-trial-devhub –setasreleased and got the expected message:

Successfully updated the package version. ID: 05i1I000000001nQAA.

But even with a released package, I was still unable to install the package into my DX Trial Org’s Dev Hub.

So next I created a new Scratch Org and for about 4 hours I got the same messages when trying to install the package and check its status. The first two attempts to run sfdx force:package:install -ugs-scratchorg01 -i 04t1I000003cm03QAA resulted in the same status messages as above.

Finally after the 3rd attempt at installing the package in the new Scratch Org  I logged in to the org and I was able to see the Installed Package, and when I returned to the CLI I got the happy success message when I ran sfdx force:package:install:get -i 0Hf3D0000008SUeSAM -u gs-scratchorg01

Successfully installed package [04t1I000003cm03QAA]

One note is that installing from the CLI apparently defaults the installation settings to “Install for All Users” because all the Profiles in the Scratch Org have read and edit access to the new field – even the Read Only Profile.

I can’t actually conclude that I did something different that solved this. It is a beta product, so maybe that’s why there was some delays.


Trailblazer Community Group “Packaging 2 Beta”: https://success.salesforce.com/_ui/core/chatter/groups/GroupProfilePage?g=0F93A000000Lg5U

Sign up for a DX Trial Org: https://developer.salesforce.com/promotions/orgs/dx-signup

DF17 Session “How Everyone Can Leverage Salesforce DX Packaging”: https://success.salesforce.com/Sessions?eventId=a1Q3A00000stRRuUAM#/session/a2q3A000000GV6nQAG

Developer Controlled Packages Description, Tips and Practice Demo: https://success.salesforce.com/0693A0000068ZKt

And, of course, TFM: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_dev2gp.htm?search_text=forceignore

Written by Always Thinkin

January 10, 2018 at 3:47 pm

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 Workshop Webinar 11: Custom Settings

leave a comment »

The video for the workshop session on Apex with Custom Settings can be found on YouTube here:

The trigger and its unit test we worked with in this session is available here:

Written by Always Thinkin

July 8, 2017 at 7:15 pm

Posted in Apex, Beginner, Code Sample

Apex Workshop Webinar 10: Exception Handling

leave a comment »

The video for the workshop session on Apex Exception Handling can be found on YouTube here:

The trigger and its unit test we worked with in this session is available here:

Written by Always Thinkin

July 8, 2017 at 7:05 pm

Posted in Apex, Beginner, Code Sample

Apex Workshop Webinar 9: Unit Tests

leave a comment »

The video for the workshop session on Apex Unit Testing can be found on YouTube here:

The trigger and its unit test we worked with in this session is available here:

Written by Always Thinkin

July 8, 2017 at 7:01 pm

Posted in Apex, Beginner, Code Sample

Apex Workshop Webinar 8: Invocable Apex

leave a comment »

The video for the workshop session on Invocable Apex Classes can be found on YouTube here:

The original source is from a presentation at Dreamforce ’16 based on https://github.com/mshanemc/processBuilderBlocks.
In our session we worked with:
PBB Lock:
PBB Delete:
PBB Utility (dedupe):

Ohad’s String Utility

Written by Always Thinkin

July 8, 2017 at 6:54 pm

Posted in Apex, Beginner, Code Sample