Archive for Tips Category Feed

Invoking Apex from a Button (JS –> Apex Web Service)

In January I posted about how to invoke Apex from a Custom Button using a Visualforce Page.  It has been a popular post and is a topic which is of interest to many developers.  I wanted to draw your attention to another post.  Sam Arjmandi, from Salesforce Source, posted about how to invoke Apex from a button by calling the Apex directly from JavaScript whereby the Apex must be available as a web service.

Both methods work just fine and there is a choice of approach.  Recently, my personal choice for implementing this kind of functionality is to use Sam’s approach and it’s mostly due to the user experience.  Using this approach, the code is called directly and there is no time spent by the browser needing to load a new blank page only to return to the same page.  It works much more seamlessly.  You also get the benefit of having less objects to develop (no VF page) and tie together.  The only real downside (outside of requirement-specific ones) is that Salesforce won’t bark at you if you delete the web service class because it doesn’t know that the web service is tied to JavaScript.  If your Apex Code is a controller for your VF page, Salesforce will protect you from deleting it accidentally.

Comments (2) comments 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

Next entries » · « Previous entries