Using and abusing the platform – hic sunt dracones (here be dragons)

I'm constantly amused and challenged by people who steadfastly believe that X cannot be done in software. Software is, by definition, not necessarily burdened by hard boundaries and limits. Put simply, I believe we can, with dedication and grit, accomplish pretty much anything with software. In this section, I want to present some use cases and code that overcome platform limitations. These use cases and code demonstrate creative and unorthodox uses of platform features. These techniques should not be front line choices for developing solutions. In fact, while these are all technically possible, there are dragons here, questions that require you to carefully consider why you might not want to use techniques like these even though you can. None of these are outright dangerous, but they are not entirely safe either.

Let's start with overcoming platform limitations during testing. If your organization utilizes API integrations calling out from within Salesforce, you'll be faced with testing a method that both a: manipulates existing data, and b: calls out to a third-party service for more information. Sadly, this is one of those situations where testing the solution is harder than the actual solution. In a testing situation, you should be inserting all the data that your method is going to need. But making a DML call—insert—will prevent any further HTTP callouts from executing within that Apex context. Even mocked callouts will throw the frustrating you have uncommitted work pending error. That means inserting a contact and then making a call out to populate some additional data from a web service just won't work. Thankfully, Salesforce provides a way to bend that rule just a bit, enough to allow us to make DML and hit a webservice, especially if mocked!

Apex gives us two tools that are helpful here. The first is the asynchronous Apex, specifically the @future annotation. Inside the context of a test, the @future annotation essentially allows you to switch Apex execution contexts. Because of the Apex context switch, governor limits and DML flags are reset. The second tool is the test method Test.stopTest(). Among the many tricks stopTest() performs is this gem: when you call Test.stopTest();, all @future methods are immediately executed. When combined together, these two tricks give us a way to both insert new data as part of our test and make mocked callouts. This allows us to test code that needs both properly generated test data, as well as information from a mocked callout to properly make decisions. Here's an example:

@future
global static void SomeMethodWithACallout(id accountId){
     TestRestClient trc = new TestRestClient();
     id aId;
     try {
          aId = (Id) accountId;
     } catch (Exception e) {
          throw new exception('You fail.');
     }
     Account a = [select id, name, bar from Account where id = :aId];

     //make your callout
     RestClientHTTPMocks fakeResponse = 
new RestClientHTTPMocks(200, 'Success', 'Success',
new Map<String,String>());
     System.AssertNotEquals(fakeResponse, null);
     Test.setMock(HttpCalloutMock.class, fakeResponse);
     System.AssertNotEquals(trc, null); 
     String result = trc.get('http://www.google.com');
}

Here in our code, we've established a @future method that makes a mocked callout. Because of its @future annotation, its understanding of DML is separate and distinct from the execution context that's inserting the required test data. Additionally, because we're going to call it from with a unit test, we can use stopTest() to force its synchronous execution. Given this test helper, we can then construct a test like this:

@isTest
static void test_method_one() {

     //If you're not using SmartFactory, you're doing it wrong
     Account account = (Account)
SmartFactory.createSObject('Account');
     insert account;
     Test.startTest();
     AwesomeClass.SomeMethodWithACallout(account.id);
     Test.StopTest();
}

Obviously, you'd want to do more than just call the mock callout method, but this demonstrates how it works. In this test, I'm making use of the RestClient we discussed previously. You could modify the RestClient class to automatically call internal @future methods when Test.IsRunningTest() is run. Doing so would allow you to write essentially unmodified best practice tests where you create your test data and execute your unit of code within startTests() and stopTest(). In hindsight, this may seem obvious and not terribly dangerous. Nevertheless, I've lost track of the number of times I've explained this technique to developers struggling with testing callouts. While it may not seem terribly dangerous, it does essentially bypass an exception condition that would prevent your code from working properly. I use this pattern all the time, but I also caveat its use with the proviso that it's not sanctioned and may stop working at the next release.

While the last one wasn't actually terribly dangerous and only affects testing, this next one is a bit more meta, and because of that, you should take care in understanding the ramifications of it. With the ability to make callouts from inside the Salesforce1 platform, we gain the ability to use virtually any API available. This includes the Salesforce1 platform APIs. This is incredibly powerful with virtually limitless uses. You can, for instance, utilize the analytics API to run a report and process the returned results into records that another report uses. Or you can use the analytics API to execute a report, merge it with an Excel template, and e-mail a report complete with pre-established pivot tables and charts to your executive team.

Perhaps more interesting, however, is the ability to invoke the tooling API from within Apex. Amongst its myriad of tricks, the tooling API allows us to query object data and execute anonymous Apex statements. If you've never used a highly dynamic language such as Smalltalk, Ruby, or Python where meta-programming is a common pattern, the ramifications of invoking execute anonymous from within Apex may not be immediately clear. Eval() is a basic metaprogramming construct that allows developers to have the computer evaluate code generated from a dynamically generated string during runtime. Let's take a step back and talk about how computers interpret our code.

Whether during compile or runtime, the programming language itself is responsible for translating human readable code into something the computer can do. What differs amongst languages is the grammar the human readable code takes.

Meta-programming is a bit of a mind bender, but the essence of it is that instead of writing code to solve one problem, developers write code that solves many problems, or, as I like to think of it, developers write code that writes code on the fly.

The idea behind Eval() in languages such as Ruby, Python, and JavaScript is to establish a compiler or interpreter construct that can accept a string of text and then read and interpret it as if it were actually code. If you're not a coder, you may still be waiting for the punch line; what makes this all very important is that, as coders, we can create that string programmatically, mixing in variables for class names, values, and so on. This allows for highly dynamic software that, in effect, is capable of writing itself during execution.

On the Salesforce1 platform, we essentially have two programming languages available to us: Apex and Javascript. Javascript is considered a dynamic language, Apex not so much. In fact, Javascript provides an Eval() method whereas Apex, on the other hand, does not. Sadly, however, Javascript is only available within the browser, so we cannot utilize its Eval() method for Apex-based API integrations. How, then, can we create a dynamic Eval() method for Apex code? Imagine a situation where a legacy computer system is able to nightly spit out a report of field information. That field information is needed to properly construct a CSV file whose columns changed nightly. When working on such a problem, we were shown the JEXL string the nightly API report would give us:

variable1 eq '1' or AwsomeVar eq '1' or AwesomeSauce eq '1' or BowTiesAreCool eq '1' or theDoctor eq '1'

JEXL, which you can see in all its glory above, is a programming language unto itself. I needed to evaluate the JEXL expressions rendering a true or false. Using string parsing functions to replace variable names and operands from static maps of data rendered the JEXL into something like this:

variable1__c == true || AwsomeVar__c eq == true || AwesomeSauce__c == true || BowTiesAreCool__c == true || theDoctor__c == true

The problem is that while I can turn that JEXL into Apex code, there's no native way to take and execute that string. If there were a way to wrap that string in an If() statement and execute it, we'd be golden. Here is where Eval() comes in handy. With Eval(), I can pass in that translated string and evaluate it within an if statement. This allows the fields included in the daily Salesforce export to be dynamically determined from the nightly JEXL output. Whenever the legacy team altered their schema, Eval() enabled the Salesforce code to adapt its data export accordingly.

So, how do we create an Eval() method? Utilizing the tooling API's rest access to (securely) call ExecuteAnonymous means we can construct an anonymous code block and have ExecuteAnonymous evaluate it just as if we were using the developer console's ExecuteAnonymous window. Note that this establishes two requirements for Apex's Eval() to work: API access, which means you can easily lock down what users are allowed to access Eval() using classes, and setting up a Remote Site in your org, which allows you to call out to your instance of Salesforce (that is, na4.salesforce.com or cs3.salesforce.com). Because Apex is a typed language, our Eval methods will need to return a specific type. In my original use case, I wanted to know the Eval'd result of a Boolean expression. To do so, I created a new class, Dynamic, and used an excellent tooling API wrapper from Andrew Fawcett (https://github.com/afawcett/apex-toolingapi) to build the following Eval() method:

public class Dynamic {
    public class IntentionalException extends Exception{}

    public static boolean eval(String toEval){
        boolean result = false;
        if(!toEval.startsWith('if')) {
            toEval = 'if(' + toEval + ') {throw new Dynamic.IntentionalException('true');} else {throw new Dynamic.IntentionalException('false');}';
        }
        ToolingAPI x = new ToolingAPI();
        try{
            ToolingAPI.ExecuteAnonymousResult toolingResult =
x.executeAnonymousUnencoded(toEval);
        } catch (IntentionalException ie) {
            result = (ie.getMessage() == 'true') ? True : False;
        }
        return result;
    }
}

I'm using an exception so that I can capture and return typed data from the ExecuteAnonymous call. That way, our regular code can listen for and catch a particular type of exception, in this case, IntentionalException when successful, while still retaining the ability for our anonymously executed code to throw a different kind of exception. Of course, this represents only the Boolean data type, but you could just as easily return JSON serialized representations of records, or even arrays of IDs.

The Eval() is just the start. With the tooling API at our command from within Apex, we can even dynamically generate classes. One of the open source packages I created and maintain is called Custom Related Lists, and it allows users to mimic related lists but with the added ability to insert filters. In other words, you can create a related list that excludes inactive contacts or opportunity line items that are backordered. This work is accomplished by using Visualforce as a templating language and the tooling API to write controllers, Visualforce pages, and unit tests.

The user selects the master object whose detail pages this list will display, and the detail object whose records will be displayed on the list. Once selected, the user can select the fields to display, as well as define criteria filters. The wizard is intentionally dynamic; selecting the master object automatically determines which objects relate to it and populates the detail selection list with related objects. Once the user has specified the details, the page controller first saves the record and then generates the controller, test, and Visualforce pages needed:

Using and abusing the platform – hic sunt dracones (here be dragons)

Fundamentally, I'm (ab)using Visualforce as a template system to abstract out the boilerplate bits of Apex and Visualforce necessary to create the related lists. It consists of three Visualforce template files:

  • CRL_MetaGenCtrl: The controller template
  • CRL_MetaGenPage: The Visualforce page template
  • CRL_MetaGenTests: The Apex tests for the controller

Each of these files uses the CRL_MetaGenCtrl controller, which is responsible for providing the non-boilerplate bits of code generated from the templates I mentioned above, using a little-known pageReference method—getContents(). getContents() renders the given Visualforce page into a string as if it were requested by a browser.

Note

Note that in the latest API versions, getContents() is treated as a callout.

This allows us to use Visualforce as a templating system. Tied to a custom controller, the templates mentioned above are rendered in Apex through getContents() and result in dynamically generated Apex code with the user-selected options merged in. For example, to be included on the detail page of the master object, the Visualforce page that displays the list must extend the standard controller of the master object. The template for the page references a controller extension for the related list, as well as the standard controller, for the master object. Given our example of accounts and contacts, the custom related list page has this page tag:

<apex:page standardController="Account" extensions="CRL_conOnAct">

Similarly, the CRL_MetaGenCtrl has a method to generate the startPageTag, which is used to dynamically specify the standard controller and extensions to the final Visualforce page with standard merge syntax {!startPageTag}.

After the custom controller extension, test, and Visualforce page are rendered to strings, the application puts the code in place. Apex code is placed with the tooling API and the Visualforce page is placed with a standard REST call because of how custom related lists uses the tooling API. It's important to note that generating the code will only work in sandboxes and developer orgs. Running the generated code to display a custom related list, however, works in all orgs.

There are, of course, other considerations to keep in mind when doing this kind of out-of-the-box engineering. It would be a pain to regenerate the code and deploy a new change set every time we wanted to adjust what fields are displayed in the list. To prevent that need, the generated code references a custom related list object record upon page load. This allows admins to change the criteria or modify field sets without having to regenerate the code and deploy it. However, this also means that users would have to recreate the record in the production org. To prevent this need, the generated code contains a JSON encoded version of the initial Related_Lists__c record. After deployment to production, on the first display of the related list, the code will deserialize the JSON and create the needed record. Keeping your code, even your dynamically generated code, portable makes the entire experience much smoother for the user.

When using Visualforce as a templating system, it's important to consider what you're going to do with the resulting code. To actually take and deploy the code generated by the template, you'll need to ensure your template's page tag specifies a contentType of text/text like this:

<apex:page showHeader="false"
  sidebar="false"
  standardController="Related_List__c"
  extensions="CRL_MetaGeneratorCtrl"
  contentType="text/text"
>

Generating code with Visualforce is a neat trick, but it's useless if you can't get it back into your org. To generate and deploy the code, I use this class:

public with sharing class CRL_CodeGenerationLib {
private static ToolingAPI tooling = new ToolingAPI();
  @TestVisible private static Map<String, String> pageDetails;

  public static boolean GenerateClass(String body) {
    system.debug('Generating Class');
    ToolingAPI.ApexClass apexClass = new ToolingAPI.ApexClass();
    apexClass.Body = body;
    ToolingAPI.SaveResult sr = tooling.createSObject(apexClass);
    system.debug(sr);

    return true;
  }

  public static boolean generatePage(String pageName, String body) {
    System.debug('Generating Page');
    String salesforceHost = System.Url.getSalesforceBaseURL().toExternalForm();
    String url =  salesforceHost + '/services/data/v29.0/sobjects/ApexPage';
    HttpRequest req = new HttpRequest();

    //Sanitize the input'd page name to strip whitespace and replace spaces with _'s
    pageName = pageName.trim();
    pageName = pageName.replaceAll(' ', '_');

    pageDetails = new Map<String, String>();
    pageDetails.put('Name', pageName);
    pageDetails.put('Markup', body);
    pageDetails.put('ControllerType', '1');
    pageDetails.put('MasterLabel', pageName);
    pageDetails.put('ApiVersion', '33.0');
    String PagePayload = JSON.serialize(pageDetails);

    req.setMethod('POST');
    req.setEndpoint(url);
    req.setHeader('Content-type', 'application/json');
    req.setHeader('Authorization', 'Bearer ' + UserInfo.getSessionId());
    req.setBody(PagePayload);

    Http http = new Http();

    HTTPResponse res = http.send(req);
    System.debug('Page Generation Response: ' + res.getBody());

    return true;
  }

}

I found it interesting to learn that I could generate a custom Visualforce page simply by posting the page contents to the proper URL. Creating the Apex classes and tests requires the tooling API wrapper I discussed earlier.

Finally, I want to demonstrate how you can use language constructs to solve oddly difficult problems. I was once faced with the need to merge contact information into a block of text outside of a Visualforce page. In fact, I essentially needed to do a mail merge without sending any e-mails before doing some additional processing. Unfortunately, there's no built-in way to do that. Salesforce's Visualforce e-mail template API doesn't give you a "getter" for the merge result. Instead, the normal workflow looks like this:

    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    String[] toAddresses = new String[]{'[email protected]'};
    mail.setToAddresses(toAddresses);
    mail.setUseSignature(this.useSig);
    mail.setSaveAsActivity(this.saveActivity);
    mail.setSenderDisplayName(this.senderDisplayName);
    mail.setTargetObjectId(targetObjectId);
    mail.setTemplateId(templateId);
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});

There's not even a .merge() method exposed in Apex. The merging happens as part of Messaging.sendEmail();.

I decided to wrap the workaround I found in a reusable class: MailUtils.cls. The MailUtils.cls offers a single static method: getMergedTemplateForObjectWithoutSending(Id targetObjectId, Id templateId, Boolean useSig, Boolean saveActivity, and String senderDisplayName). That takes the work out of this. It returns a map with the following keys:

  • textBody: Merged text body
  • htmlBody: Merged HTML version
  • subject: Subject line of the e-mail

Here's MailUtils.cls in its full glory:

public class mailUtils {
  public class mailUtilsException extends exception {}

  public Boolean useSig {get; private set;}
  public Boolean saveActivity {get; private set;}
  public String senderDisplayName {get; private set;}

  public mailUtils(Boolean useSig, Boolean saveActivity, String senderDisplayName){
    this.useSig = usesig;
    this.saveActivity = saveActivity;
    this.senderDisplayName = senderDisplayName;
  }

  // Derived from: 
  // http://salesforce.stackexchange.com/questions/13/using-apex-to-assemble-html-letterhead-emails/8745#8745
  public Messaging.SingleEmailMessage MergeTemplateWithoutSending(Id targetObjectId, Id templateId) {
    Messaging.reserveSingleEmailCapacity(1);
    Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
    // Intentionally set a bogus email address.
    String[] toAddresses = new String[]{'[email protected]'};
    mail.setToAddresses(toAddresses);
    mail.setUseSignature(this.useSig);
    mail.setSaveAsActivity(this.saveActivity);
    mail.setSenderDisplayName(this.senderDisplayName);
    mail.setTargetObjectId(targetObjectId);
    mail.setTemplateId(templateId);

    // create a save point
    Savepoint sp = Database.setSavepoint();
    // Force the merge of the template.
    Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
    // Force a rollback, and cancel mail send.
    Database.rollback(sp);

    // Return the mail object
    // You can access the merged template, subject, etc. via:
    // String mailTextBody = mail.getPlainTextBody();
    // String mailHtmlBody = mail.getHTMLBody();
    // String mailSubject = mail.getSubject();
    return mail;

  }

  public static Map<String,String> getMergedTemplateForObjectWithoutSending(Id targetObjectId, Id templateId, Boolean useSig, Boolean saveActivity, String senderDisplayName) {
    Map<String,String> returnValue = new Map<String,String>();
    mailUtils mu = new mailUtils(useSig, saveActivity, senderDisplayName);
    Messaging.SingleEmailMessage mail = mu.MergeTemplateWithoutSending(targetObjectId, templateId);
    returnValue.put('textBody', mail.getPlainTextBody());
    returnValue.put('htmlBody', mail.getHTMLBody());
    returnValue.put('subject', mail.getSubject());
    return returnValue;
  }

}

I draw attention to this use case because of the way it uses a savepoint and rollback to capture the results of the e-mail merge. It exposes the way a DML rollback doesn't roll back the state of a given object. Once the rollback occurs, we have access to a merged Messaging.SingleEmailMessage instance. Getting the text body, HTML body, and subject is as simple as calling their getters.

Again, I want to state you should adopt patterns like these after careful consideration of the ramifications. These are incredibly powerful, but unsafe in the wrong hands.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.186.79