Search

Sunday 23 December 2012

Pass By Reference or By Value in Salesforce

You probably know all or most of this but try this

//If you want to know definitely what happens to references in SF run callRefs(); - http://shivasoft.in/blog/java/pass-by-value-and-pass-by-reference/

public static void callRefs(){
Boolean testbool = true;
integer testint=1;
Map<String,String> testmap1 = new Map<String,String>();
Map<String,list<String>> testmap2 = new Map<String,list<String>>();
Account acc = [Select BillingCountry From Account Limit 1];
Map<String,list<Error__c>> testmap3 = new Map<String,list<Error__c>>();
Map<String,set<String>> testmap4 = new Map<String,set<String>>();
testRefs(testbool, testint, testmap1, testmap2, testmap3, testmap4, acc);
system.debug('## testbool ' + testbool);
system.debug('## testint ' + testint);
system.debug('## testmap1 ' + testmap1);
system.debug('## testmap2' + testmap2);
system.debug('## testmap3' + testmap3);
system.debug('## testmap4' + testmap4);
system.debug('## acc ' + acc);
Error__c err2 = new Error__c(Class_Name__c='test2', SOQL_Queries__c=2);
list<Error__c> changelst = testmap3.get('testme');
changelst.add(err2);
testmap3.put('testme', changelst);
testRefs(testbool, testint, testmap1, testmap2, testmap3, testmap4, acc);
system.debug('## testmap3' + testmap3);
system.debug('## testmap4' + testmap4);
}
public static void testRefs(Boolean testbool, integer testint, Map<String,String> testmap1, Map<String,list<String>> testmap2, Map<String,list<Error__c>> testmap3, Map<String,set<String>> testmap4, Account acc){
testbool = false;
testint=2;
testmap1.put('test' , '1');
list<String> newlst = new list<String>();
newlst.add('1');
newlst.add('2');
testmap2.put('testme' , newlst);
acc.BillingCountry = 'Spain';
update acc;
list<Error__c> errlst;
if (testmap3.get('testme') == null){//if you remove this if something different will happen
Error__c err = new Error__c(Class_Name__c='test', SOQL_Queries__c=1);
errlst = new list<Error__c>();
errlst.add(err);
testmap3.put('testme', errlst);
}
set<String> newset = new set<String>();
newset.add('hi');
testmap4.put('test', newset);
}
Salesforce state
“In Apex, all primitive data type arguments, such as Integer or String, are passed into methods by value. This means that any changes to the arguments exist only within the scope of the method. When the method returns, the changes to the arguments are lost.
Non-primitive data type arguments, such as sObjects, are also passed into methods by value. This means that when the method returns, the passed-in argument still references the same object as before the method call, and can’t be changed to point to another object. However, the values of the object’s fields can be changed in the method.”

Sunday 25 November 2012

REST Call To Enterprise WSDL From Apex Class


First of all I need to give you a bit of background to why I would even want to do this.
I had a problem where I needed to get all the objects and their fields of an org and their full xml, similar to what you see in Eclipse. This was going to be part of a new App for the Appexchange I was working on so it needed to work in any org of any data model.
So the obvious problem here would be that I couldn't determine how many objects an org actually had, so if I used Schema.describes I could easily hit 100 limit of calls.
So I decided that I would need to use the Enterprise wsdl which is a strictly typed api containing the full data model of the org. Perfect! But I didnt want to call out of Salesforce to some service, just to call back to give me the results. I wanted this to be completely native to Salesforce.

The solution as a theory would be to use Rest to call the Enterprise wsdl natively within Salesforce.
Problem with this is many fold. What is the xml that needs to be sent to Rest? What is the best way of connecting to Salesforce, I could use Userinfo.getSessionid(), but that wouldn't necessarily guarantee that the user had api enabled. So I decided to use the partner wsdl to first connect to Salesforce with a specific user, which will be gauranteed to always be active, always have api enabled and be a sys admin to have access to the entire data structure.

So first I needed to consume the partner wsdl in Salesforce. Well I worked out a why of doing this with a bit of messing around and then later I found an article that explained what to do,luckily it was exactly what I did.

http://developer.force.com/cookbook/recipe/calling-salesforce-web-services-using-apex

Now I could login into Salesforce with a user I specify

String session_ID;
       partnerSoapSforceCom.Soap myPartnerSoap;
if (myPartnerSoap == null) {
RestLocalLogin__c locallogin = RestLocalLogin__c.getInstance('login'); //custom setting
system.debug('###login ' +locallogin.username__c + ' '+locallogin.password__c);
if (locallogin != null){
myPartnerSoap = new partnerSoapSforceCom.Soap();//partner wsdl consumed

// call login
partnerLoginResult = myPartnerSoap.login(locallogin.username__c,locallogin.password__c);
system.debug('###partnerLoginResult ' + partnerLoginResult);
// from here, the session id can be accessed via partnerLoginResult.sessionId

// now construct a SessionHeader element for your webservice and set the session id
system.debug('session ' +partnerLoginResult);

session_ID = partnerLoginResult.sessionId;
}
else
session_ID = userinfo.getSessionId();
}



I looked at the Salesforce documentation of how to make a Rest call to the Enterprise wsdl. My advice, dont, because the Salesforce documentation is WRONG. I spent some time just trusting Salesforce on this but my good friend Tomasz Duda pointed out that the Salesforce documentation was incorrect.

With a bit of playing around as a test I got:-

Http http = new Http();
HttpRequest req = new HttpRequest();
req.setEndpoint('https://cs7.salesforce.com/services/Soap/c/26.0');
req.setMethod('POST');
req.setHeader('Content-Type', 'text/xml; charset=utf-8');
req.SetHeader('SOAPAction','""');

String body = '<?xml version="1.0" encoding="utf-8"?>' +
+'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">' +
+'<soap:Header><urn:SessionHeader><urn:sessionId>' + UserInfo.getSessionId() +'</urn:sessionId></urn:SessionHeader></soap:Header>'
+'<soap:Body><urn:describeGlobal /></soap:Body></soap:Envelope>';

req.setBody(body);

HTTPResponse res = http.send(req);
String output = res.getBody();
System.debug(output);


Now I needed to construct the xml to make specific calls. So first I needed to consume the enterprise wsdl in Salesforce so I could see how it was constructed. Well I could have made a java class outside of Salesforce using the wsdl, but I was intrigued to see if I could do this as well.


To consume enterprise wsdl into SF directly you get this error
Unsupported schema type: {http://www.w3.org/2001/XMLSchema}anyType

So follow these steps
1. Export enterprise wsdl
2. Replace all occurrences of anyType with 'string' in enterpriseSoapSforceCom
3. Now try to create apex classes from wsdl - this will fail but you will be able to copy contents of each apex class it tries to create
4. Go to Eclipse create class sobjectEnterpriseSoapSforceCom but comment out all public enterpriseSoapSforceCom and save
5. Then change any methods like merge() etc in enterpriseSoapSforceCom to mergeRecord() which cause problems, then save
6. Go back to sobjectEnterpriseSoapSforceCom class and remove comments and save again

Done

Now you can see easily the individual methods in the Apex class and you can now easily construct your Rest xml requests.

Note: Replace the UserInfo.getSessionId() with the session id from logging in using the partner wsdl
This Rest call with grab all field xml for custom objects VVV__c and XXX__c


String body = '<?xml version="1.0" encoding="utf-8"?>' +
+'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">' +
+'<soap:Header><urn:SessionHeader><urn:sessionId>' + UserInfo.getSessionId() +'</urn:sessionId></urn:SessionHeader></soap:Header>'
+'<soap:Body><urn:describeSObjects><urn:sObjectType>VVV__c</urn:sObjectType><urn:sObjectType>XXX__c</urn:sObjectType></urn:describeSObjects></soap:Body></soap:Envelope>';

Note: describeSObjects is the name of the function call in the consumed Enterprise wsdl class and sObjectType is the name of the parameter passed to the function


This Rest call will get the fields xml for just 1 object for Project__c - The function is describeSObject()

String body = '<?xml version="1.0" encoding="utf-8"?>' +
+'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">' +
+'<soap:Header><urn:SessionHeader><urn:sessionId>' + UserInfo.getSessionId() +'</urn:sessionId></urn:SessionHeader></soap:Header>'
+'<soap:Body><urn:describeSObject><urn:sObjectType>Project__c</urn:sObjectType></urn:describeSObjects></soap:Body></soap:Envelope>';


This Rest call can be used to get names of all objects in the org - The function is describeGlobal()

String body = '<?xml version="1.0" encoding="utf-8"?>' +
+'<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">' +
+'<soap:Header><urn:SessionHeader><urn:sessionId>' + UserInfo.getSessionId() +'</urn:sessionId></urn:SessionHeader></soap:Header>'
+'<soap:Body><urn:describeGlobal /></soap:Body></soap:Envelope>';




Wednesday 7 November 2012

Testing Web Service Callouts

Generated code is saved as an Apex class containing the methods you can invoke for calling the Web service. To deploy or package this Apex class and other accompanying code, 75% of the code must have test coverage, including the methods in the generated class. By default, test methods don’t support Web service callouts and tests that perform Web service callouts are skipped. To prevent tests from being skipped and to increase code coverage, Apex provides the built-in WebServiceMock interface and theTest.setMock method that you can use to receive fake responses in a test method.

Specifying a Mock Response for Testing Web Service Callouts

When you create an Apex class from a WSDL, the methods in the auto-generated class call WebServiceCallout.invoke, which performs the callout to the external service. When testing these methods, you can instruct the Apex runtime to generate a fake response whenever WebServiceCallout.invoke is called. To do so, implement the WebServiceMock interface and specify a fake response that the Apex runtime should send. Here are the steps in more detail.
First, implement the WebServiceMock interface and specify the fake response in the doInvoke method.
global classYourWebServiceMockImplimplements WebServiceMock {
   global void doInvoke(
           Object stub,
           Object request,
           Map<String, Object> response,
           String endpoint,
           String soapAction,
           String requestName,
           String responseNS,
           String responseName,
           String responseType) {

        // Create response element from the autogenerated class. 
    
        // Populate response element. 
    
        // Add response element to the response parameter, as follows: 
    
        response.put('response_x', responseElement); 
   }
}
Note
  • The class implementing the WebServiceMock interface can be either global or public.
  • You can annotate this class with @isTest since it will be used only in test context. In this way, you can exclude it from your organization’s code size limit of 3 MB.
Now that you have specified the values of the fake response, instruct the Apex runtime to send this fake response by calling Test.setMock in your test method. For the first argument, pass WebServiceMock.class, and for the second argument, pass a new instance of your interface implementation of WebServiceMock, as follows:
Test.setMock(WebServiceMock.class, newYourWebServiceMockImpl());
After this point, if a Web service callout is invoked in test context, the callout is not made and you receive the mock response specified in your doInvoke method implementation.
Note
If the code that performs the callout is in a managed package, you must call Test.setMock from a test method in the same package with the same namespace to mock the callout.
This is a full example that shows how to test a Web service callout. The implementation of the WebServiceMock interface is listed first. This example implements thedoInvoke method, which returns the response you specify. In this case, the response element of the auto-generated class is created and assigned a value. Next, the response Map parameter is populated with this fake response. This example is based on the WSDL listed in Understanding the Generated Code. Import this WSDL and generate a class called docSample before you save this class.
@isTest
global class WebServiceMockImpl implements WebServiceMock {
   global void doInvoke(
           Object stub,
           Object request,
           Map<String, Object> response,
           String endpoint,
           String soapAction,
           String requestName,
           String responseNS,
           String responseName,
           String responseType) {
       docSample.EchoStringResponse_element respElement = 
           new docSample.EchoStringResponse_element();
       respElement.EchoStringResult = 'Mock response';
       response.put('response_x', respElement); 
   }
}
This is the method that makes a Web service callout.
public class WebSvcCallout {
    public static String callEchoString(String input) {
        docSample.DocSamplePort sample = new docSample.DocSamplePort();
        sample.endpoint_x = 'http://api.salesforce.com/foo/bar';
        
        // This invokes the EchoString method in the generated class 
    
        String echo = sample.EchoString(input);
        
        return echo;
    }   
}
This is the test class containing the test method that sets the mock callout mode. It calls the callEchoString method in the previous class and verifies that a mock response is received.
@isTest
private class WebSvcCalloutTest {
    @isTest static void testEchoString() {              
        // This causes a fake response to be generated 
    
        Test.setMock(WebServiceMock.class, new WebServiceMockImpl());
        
        // Call the method that invokes a callout 
    
        String output = WebSvcCallout.callEchoString('Hello World!');
        
        // Verify that a fake result is returned 
    
        System.assertEquals('Mock response', output); 
    }
}

Friday 26 October 2012

Using Ant To Deploy


In addition to the Force.com IDE, you can also use a script to deploy Apex.
Download the Force.com Migration Tool if you want to use a script for deploying Apex from a Developer Edition or sandbox organization to a Database.com production organization using Apache's Ant build tool.
http://www.salesforce.com/us/developer/docs/apexcode/Content/images/help/helpNote_icon.gifThe Force.com Migration Tool is a free resource provided by salesforce.com to support its users and partners but isn't considered part of our services for purposes of the salesforce.com Master Subscription Agreement.
To use the Force.com Migration Tool, do the following:
1.     Visit http://java.sun.com/javase/downloads/index.jsp and install Java JDK, Version 6.1 or greater on the deployment machine.
2.     Visit http://ant.apache.org/ and install Apache Ant, Version 1.6 or greater on the deployment machine. i found it here http://ant.apache.org/bindownload.cgi

3.     Set up the environment variables (such as ANT_HOMEJAVA_HOME, and PATH) as specified in the Ant Installation Guide at http://ant.apache.org/manual/install.html. Dont forget setup the same variables in User and System environment variables. In windows right click on Computer > Properties > Advanced Settings > environment variables

4.     Verify that the JDK and Ant are installed correctly by opening a command prompt, and entering ant –version. Your output should look something like this:
Apache Ant version 1.7.0 compiled on December 13 2006
5.     Log in to Salesforce on your deployment machine. Click Your Name | Setup | Develop | Tools, then click Force.com Migration Tool.
6.     Unzip the downloaded file to the directory of your choice. The Zip file contains the following:
o    Readme.html file that explains how to use the tools
o    A Jar file containing the ant task: ant-salesforce.jar
§  A sample folder containing:
§  codepkg\classes folder that contains SampleDeployClass.cls and SampleFailingTestClass.cls
§  codepkg\triggers folder that contains SampleAccountTrigger.trigger
o    mypkg\objects folder that contains the custom objects used in the examples
o    removecodepkg folder that contains XML files for removing the examples from your organization
o    A sample build.properties file that you must edit, specifying your credentials, in order to run the sample ant tasks in build.xml
o    A sample build.xml file, that exercises the deploy and retrieveAPI calls
7.     Copy the ant-salesforce.jar file from the unzipped file into the ant lib directory. The ant lib directory is located in the root folder of your Ant installation.
8.     Open the sample subdirectory in the unzipped file.
9.     Edit the build.properties file:
§  Enter your Salesforce production organization username and password for the sf.username and sf.password fields, respectively.Note
o    http://www.salesforce.com/us/developer/docs/apexcode/Content/images/help/helpNote_icon.gifThe username you specify should have the authority to edit Apex.
o    If you are using the Force.com Migration Tool from an untrusted network, append a security token to the password. To learn more about security tokens, see “Resetting Your Security Token” in the Salesforce online help.
§  If you are deploying to a sandbox organization, change the sf.serverurl field to https://test.salesforce.com.
10.   Open a command window in the sample directory.
11.   Enter ant deployCode. This runs the deployAPI call, using the sample class and Account trigger provided with the Force.com Migration Tool.
The ant deployCode calls the Ant target named deploy in the build.xml file.
<!-- Shows deploying code & running tests for package 'codepkg' -->
    <target name="deployCode">
      <!-- Upload the contents of the "codepkg" package, running the tests for just 1 class -->
      <sf:deploy username="${sf.username}" password="${sf.password}" serverurl="${sf.serverurl}" deployroot="codepkg">
        <runTest>SampleDeployClass</runTest>
      </sf:deploy>
    </target>
For more information on deploy, see Understanding deploy.
12.   To remove the test class and trigger added as part of the execution of ant deployCode, enter the following in the command window: ant undeployCode.
ant undeployCode calls the Ant target named undeployCode in the build.xml file.
<target name="undeployCode">
      <sf:deploy username="${sf.username}" password="${sf.password}" serverurl=
            "${sf.serverurl}" deployroot="removecodepkg"/>
</target>

Tuesday 23 October 2012

Really Useful Info On MetaData


Here are all the different meta data types in Salesforce. Why is this useful, because if you are wanting to deploy just a few fields by Eclipse, instead of just selecting the Object in Eclipse if you change the package.xml to below it will just deploy the specific fields you select

     <types>
        <members>X__c.YYY__c</members> //X__c.YYY__c is object.field
        <name>CustomField</name>
    </types>

http://www.salesforce.com/us/developer/docs/api_meta/index.htm

Here is the complete list of types in an Objec, some of which can be deployed using Eclipse, ant or changesets separately from the Object
http://www.salesforce.com/us/developer/docs/api_meta/Content/customobject.htm
http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_workflow.htm
http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_layouts.htm


Unfortunately there are many things that cannot be deployed using Eclipse, ant or changesets which are listed below. Come on Salesforce some of these things are needed.



  • Account Teams
  • Activity Button Overrides
  • Activity Settings
  • Analytic Settings
  • Approval Processes
  • Auto-number on Customizable Standard Fields
  • Business Hours
  • Campaign Influences
  • Case Assignment Rules
  • Case Contact Roles
  • Case Escalation Rules
  • Case Feed Layouts
  • Case Team Roles
  • Chatter Approvals
  • Chatter Settings
  • Console Layouts
  • Contract Line Items
  • Contract Settings
  • Currency Exchange Rates
  • Data Category Visibility Settings
  • Delegated Administration
  • Divisions
  • Email Services
  • Email Settings
  • Email-to-Case
  • Entitlement Processes
  • Entitlement Settings
  • Entitlement Templates
  • Field History Tracking – Currency and Owner Fields
  • Fiscal Year
  • Forecasts
  • Holidays
  • HTML Document and Attachment Settings
  • Ideas Comment Validation Rules
  • Ideas Settings
  • Label Renames
  • Lead Assignment Rules
  • Lead Settings
  • Mail Merge Templates
  • Milestones
  • Mobile Administration
  • Mobile Users and Devices
  • Offline Briefcase Configurations
  • Opportunity Big Deal Alerts
  • Opportunity Competitors
  • Opportunity Sales Processes
  • Opportunity Settings
  • Opportunity Team and Account Team Roles
  • Opportunity Update Reminders
  • Organization Wide Email Addresses
  • Partner Management
  • Predefined Case Teams
  • Product Schedule Setup
  • Product Settings
  • Public and Resource Calendars
  • Quote Templates
  • Salesforce to Salesforce
  • Search Layouts on Standard Objects
  • Search Settings
  • Self-Service Portal Font and Colors
  • Self-Service Portal Settings
  • Self-Service Portal Users
  • Self-Service Public Solutions
  • Self-Service Web-to-Case
  • Sharing Organization Wide Defaults
  • Sites.com
  • Social Account/Contact Settings
  • Solution Categories
  • Solution Settings
  • Support Auto-Response Rules
  • Support Settings
  • Tab renames
  • Tag Settings
  • Territory Assignment Rules
  • User Interface Settings
  • Web Links on Person Account Page Layouts
  • Web-to-Lead
  • Web-to-Lead Auto-Response

    •  
    See http://www.salesforce.com/us/developer/docs/api_meta/Content/meta_unsupported_types.htm for a complete list which may change