Archive for APEX Code Category Feed

Force.com Debug Log Parser

Kyle Peterson has released the Force.com Debug Log Parser, a nifty little .NET app for parsing the mess of a debug log you get from Salesforce when executing Apex code.   You can see a video demonstration here.

Great work, Kyle!

P.S. The comments to the post suggest an interest by Salesforce to include this into the IDE.  Hopefully that happens.  Until then, I’ll install this version and use it regularly.

Comments (1) comments feed

Testing HTTP Callouts

When writing Apex Code, there are some things that simply cannot be tested, namely HTTP callouts.  The Intro to Apex Code Test Methods has one solution to testing as much of an HTTP callout as possible.  That is to break the HTTP callout into 3 methods: 1) Build Request, 2) Make Request, 3) Handle Response.  In the test method, you are only calling the Build Request and Handle Response methods.

I have another approach.  My main reason for not using the approach mentioned above is that I didn’t read that post until after I developed something.  However, my approach accomplishes the same thing, but also adds a variable to your class so that you can know whether or not a test is running.  Knowing this may be useful in other situations too.

Here’s is a class that uses my technique.

public class sample {

	// Static variable that assumes a test is not running
	public static boolean isApexTest = false;
	
	public String main(){
	    
	    // Do a whole bunch of stuff
	    
		// Build the http request
		Http h = new Http();
		HttpRequest req = new HttpRequest();
	    req.setEndpoint('http://local.yahooapis.com/MapsService/V1/geocode?appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--&street=701+First+Ave&city=Sunnyvale&state=CA'); 
	    req.setMethod('GET');
	    
		// Invoke web service call
		String result = '';
		if (!isApexTest){
			// Make a real callout since we are not running a test
			HttpResponse res = h.send(req);
			result = res.getBody();
		} else {
			// A test is running
			result = '<?xml version="1.0"?><ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"><Result precision="address"><Latitude>37.416397</Latitude><Longitude>-122.025055</Longitude><Address>701 1st Ave</Address><City>Sunnyvale</City><State>CA</State><Zip>94089-1019</Zip><Country>US</Country></Result></ResultSet>';
		}
			
		// Do whole bunch of stuff
	    
	    return result;	    
	}
	
	// Wrapper method for "main" that we will call in the Test Class
	public String mainForTest(){
		isApexTest = true;
		return main();
	}
}

I have a static variable called isApexTest that I set to false by default so the code assumes a test is not running.  For testing purposes, I created a “wrapper” method (called “mainForTest()”) that I will call in my test class in order to test the main() method. This wrapper method sets the isApexTest variable to true, calls the main() method and then passes back the result from the main() method. mainForTest() is merely a way to get the isApexTest variable set to true.

My test class is below.

@isTest 
private class sample_test {

    static testMethod void main_test() {
	 	
		// Establish sample class
		sample s = new sample();
		
		// Call the mainForTest wrapper method so we can set the variable indicating that a test is running
		String retVal = s.mainForTest();
	 	
	 	// Add asserts for validation
	 	String expectedResult = '<?xml version="1.0"?><ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"><Result precision="address"><Latitude>37.416397</Latitude><Longitude>-122.025055</Longitude><Address>701 1st Ave</Address><City>Sunnyvale</City><State>CA</State><Zip>94089-1019</Zip><Country>US</Country></Result></ResultSet>';
	 	System.assertEquals(retVal, expectedResult);
	 	
	 }
	 
}

In case you are wondering why I use a static variable for isApexTest instead of a class property, it is because I originally developed this technique to test an Apex Web Service. Since all web service methods are static, I couldn’t create an instance of a class to set the property to true and then call the web service method. Web service methods must be invoked directly and not through an instance of a class. That’s where the wrapper method idea came from.

Another approach than a wrapper method would be to have an input variable to my main() method that accepts true/false whether a test was running.  I did not go with this approach because I might have many methods performing callouts and I preferred to have 1 public variable accessible throughout the entire class than a local variable in each method. If I used a local variable, I would need to keep passing it around to other methods.

Comment with your thoughts and alternative approaches.

Comments (17) comments feed

Set Defaults for Opportunity Contact Roles (when converting)

It’s always bugged me that the Primary flag and a Role are not set on Opportunity Contact Roles when converting a Lead.  After every convert, I manually set these.  Typically, I set the Role to “Decision Maker”and the Primary Flag to True.  I got sick of doing this manually, so I wrote a trigger for it and I am sharing it with you all.

Trigger

trigger Opportunities on Opportunity (after insert) {

	Opportunities o = new Opportunities();
	o.SetContactRoleDefaults(Trigger.new);
    
}

Class

public class Opportunities {

    // Default Constructor
    public Opportunities()
    {
    }
    
    // Sets default values on the Contact Role record created during Conversion. Called on AFTER INSERT
    public void SetContactRoleDefaults(Opportunity[] opptys) 
    {
    	/***************
        * Variables
        ***************/
		set<ID> set_opptyIDs = new set<ID>();
		
		/***************
        * Initial Loop
        ***************/
        // Get a set of the Opportunity IDs being affected.
		for(Opportunity o:opptys) { 
			set_opptyIDs.add(o.id);
		}
		
		/***************
        * Process Records
        ***************/
        // Update the Contact Role record to be Primary and the Decision Maker
        list<OpportunityContactRole> list_opptyContactRolesToUpdate = new list<OpportunityContactRole>();
		for(OpportunityContactRole ocr:[select Id,IsPrimary,Role from OpportunityContactRole where OpportunityId in :set_opptyIDs]) { 
			ocr.IsPrimary = true;
			ocr.Role = 'Decision Maker';
			list_opptyContactRolesToUpdate.add(ocr);
		}
		
		if (list_opptyContactRolesToUpdate.size() > 0) {
			update list_opptyContactRolesToUpdate;
		}
		
    }
}

Test Class

@isTest 
private class Opportunities_Test {

    static testMethod void SetContactRoleDefaults_Test() {
        
        // Create a Lead
        Lead l = new Lead();
        l.lastname = 'Lastname';
        l.firstname = 'FirstName';
        l.company = 'Company';
        insert l;
        
        // Convert the Lead
        Database.LeadConvert lc = new database.LeadConvert();
		lc.setLeadId(l.id);
		
		LeadStatus convertStatus = [Select Id, MasterLabel from LeadStatus where IsConverted=true limit 1];
		lc.setConvertedStatus(convertStatus.MasterLabel);
		
		Database.LeadConvertResult lcr = Database.convertLead(lc);
		System.assert(lcr.isSuccess());
		
		// Query Contact Role Records and Asserts all was set correctly.
		for (OpportunityContactRole ocr:[select Id,IsPrimary,Role from OpportunityContactRole where OpportunityId = :lcr.getOpportunityId()]) {
			system.AssertEquals('Decision Maker', ocr.Role);
			system.AssertEquals(true, ocr.IsPrimary);
		} 
        
    }
    
}

Comments (31) comments feed

Invoke Apex from a Custom Button using a Visualforce Page

I have had a few occasions where I wanted to invoke Apex Code by clicking a button on a Page Layout.  Everywhere I looked, it always said I needed to have the button call an s-Control, which would then invoke Apex Code that’s written as a Web Service.  I am doing my best to avoid using s-Controls and figured there had to be a way using Visualforce instead of an s-Control.  The key was the action attribute on the <apex:page tag.

Suppose you wanted to add a button on your Opportunity page that automatically did something when you pushed the button.  You can’t execute Apex Code directly from a button.  You need something in the middle.  Let’s try to do it with a Visualforce page instead of an s-Control.

Controller

The controller has the main method in it to be executed. This method returns a PageReference (very important). Note that the method does not need to be a webservice.


public class VFController {

	// Constructor - this only really matters if the autoRun function doesn't work right
	private final Opportunity o;
	public VFController(ApexPages.StandardController stdController) {
		this.o = (Opportunity)stdController.getRecord();
	}
	
	// Code we will invoke on page load.
	public PageReference autoRun() {

		String theId = ApexPages.currentPage().getParameters().get('id');

		if (theId == null) {
			// Display the Visualforce page's content if no Id is passed over
			return null;
		}

		for (Opportunity o:[select id, name, etc from Opportunity where id =:theId]) {
			// Do all the dirty work we need the code to do
		}

		// Redirect the user back to the original page
		PageReference pageRef = new PageReference('/' + theId);
		pageRef.setRedirect(true);
		return pageRef;

	}

}

Visualforce

The Visualforce page is very simple. You really don’t need anything in there except the action parameter invokes code (the autoRun method) that runs before the page is initialized. That method returns a Page Reference that redirects the user back to the original page.


<apex:page standardController="Opportunity" 
 extensions="VFController" 
 action="{!autoRun}"
>
  <apex:sectionHeader title="Auto-Running Apex Code"/>
  <apex:outputPanel >
      You tried calling Apex Code from a button.  If you see this page, something went wrong.  You should have 
      been redirected back to the record you clicked the button from.
  </apex:outputPanel>
</apex:page>

Custom Button

All we need now is a custom Opportunity button. Because we used the Opportunity standard controller in the Visualforce page, we can simply have the button point to a the page we created.

Add that button to your Page Layout and all should work swimmingly.

Comments (33) comments feed

Calling Apex Web Services from PHP

Apex Code can be exposed as a Web Service and made available outside of your Salesforce environment (e.g. from a PHP page). This approach essentially lets you build a personal API into your Salesforce org and eliminates the need for calling standard API methods in PHP code where it is vulnerable to your configuration changes.

Working with the folks over at MK Partners on some recent projects, I’ve learned how to call into Apex Web Services from PHP. It’s actually pretty easy. Special thanks for Simon Fell for helping me through a particularly tricky part.

Apex Web Service Class

A simple web service class is below. The method we call from PHP is myMethod. There are 2 inner classes that are used to capture inputs and send back outputs to PHP.

global class MyWebService {

    // A class to accept an array of input records (e.g. product/amount combinations)
    global class myInputs{
        webservice Id productId;
        webservice Double amount;
    }
    
    // A class to send back as an output to PHP
    global class myOutputs{
        webservice String errorMessage;
        webservice Boolean success;
        webservice List<myInputs> inputs;
        webservice Id contactId;
    }

    // The actual web service method we will call
    webservice static myOutputs myMethod(Id contactId, List<myInputs> inputs){
    
        /* 
        * Write a bunch of code here to do all kinds of stuff.
        */
        
        myOutputs output = new myOutputs();
            output.errorMessage = 'No errors here.';
            output.success = true;
            output.inputs = inputs;
            output.contactId = contactId;
        return output;
        
    }
}
PHP

Login like you normally would using the PHP toolkit. Nothing new here. The final part is defining some constants for use later when we call the web service.

// Include the PHP Toolkit
require_once('salesforceAPI/SforcePartnerClient.php');
require_once('salesforceAPI/SforceHeaderOptions.php');

// Login
$sfdc = new SforcePartnerClient();
$SoapClient = $sfdc->createConnection('salesforceAPI/wsdl.xml');
$loginResult = false;
$loginResult = $sfdc->login('user@domain.com', 'password' . 'securitytoken');

// Define constants for the web service. We'll use these later
$parsedURL = parse_url($sfdc->getLocation());
define ("_SFDC_SERVER_", substr($parsedURL['host'],0,strpos($parsedURL['host'], '.')));
define ("_WS_NAME_", 'MyWebService');
define ("_WS_WSDL_", _WS_NAME_ . '.xml');
define ("_WS_ENDPOINT_", 'https://' . _SFDC_SERVER_ . '.salesforce.com/services/wsdl/class/' . _WS_NAME_);
define ("_WS_NAMESPACE_", 'http://soap.sforce.com/schemas/class/' . _WS_NAME_);

Next we will call the web service. First thing to do is setup a Soap Client and modify the headers. Then we are setting up some fake data that maps to the expected inputs of the myMethod method in the web service. Then we actually call the web service.

// SOAP Client for Web Service
$client = new SoapClient(_WS_WSDL_);
$sforce_header = new SoapHeader(_WS_NAMESPACE_, "SessionHeader", array("sessionId" => $sfdc->getSessionId()));
$client->__setSoapHeaders(array($sforce_header));

// Setup fake data to send into the web service
$prodAmtArray = array();
	$prodAmtArray[] = array('productId'=>'01t60000000lvBN','amount'=>100);
	$prodAmtArray[] = array('productId'=>'01t60000000lvBS','amount'=>200);

$wrkArray = array(
				'contactId'=>'0036000000nVtpT',
				'inputs'=>$prodAmtArray
				);
				
// Call the web service
$response = $client->myMethod($wrkArray);

// Output results to browser
echo "<p><pre>" . print_r($response, true) . "</pre></p>";
echo "Contact Id is " . $response->result->contactId;

Results

Below displays what those 2 echo statements output.

stdClass Object
(
    [result] => stdClass Object
        (
            [contactId] => 0036000000nVtpTAAS
            [errorMessage] => No errors here.
            [inputs] => Array
                (
                    [0] => stdClass Object
                        (
                            [amount] => 100
                            [productId] => 01t60000000lvBNAAY
                        )
                    [1] => stdClass Object
                        (
                            [amount] => 200
                            [productId] => 01t60000000lvBSAAY
                        )
                )
            [success] => 1
        )
)
Contact Id is 0036000000nVtpTAAS
One last little trick

One annoyance during the development of Apex Web Services is having to continually generate a WSDL for the web service. When testing, I would find that I would continually need to put a new WSDL on the web server. I decided to automate this where I could add a variable to the queryString and have PHP refresh the WSDL on my web server. You need the cURL module for this, but most PHP installs have it.

$ch = curl_init();
	$fp = fopen(_WS_WSDL_, "w");
	curl_setopt($ch, CURLOPT_URL, _WS_ENDPOINT_);
	curl_setopt($ch, CURLOPT_FILE, $fp);
	curl_setopt($ch, CURLOPT_HEADER, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
	curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
	curl_setopt($ch, CURLOPT_COOKIE, 'sid='.$sfdc->getSessionId());
	setcookie("sid", $sfdc->getSessionId(), 0, "/", ".salesforce.com", 0);
	curl_setopt($ch, CURLOPT_TIMEOUT, 30);
	curl_exec($ch);
	fclose($fp);
curl_close($ch);

If you have experience with this kind of integration, please comment here on other tips & tricks.

Comments (23) comments feed

Next entries » · « Previous entries