A blog dedicated to Salesforce Ohana

Salesforce Integration with SOAP API - Part 9 - Upsert operation using Java program


Preface - This post is part of "Salesforce Integration with SOAP API" series.

In today's post, I will explain how you can do Upsert operation using SOAP API and Java Program. Before that we should know what is so special about Upsert? Upsert will allow you to create a new record or update the existing record. Now I am sure you have this question is mind - "How Salesforce will know that the record is already there in the system?", right!!!! Good. The answer is that you can pass the custom field which will be used to determine the presence of the existing record. So easy!!!

It is always recommended to use upsert() rather that create() because of the obvious reason i.e. upsert() will eliminate the chance of creating duplicate record in the system.

Syntax -
UpsertResult[] = connection.upsert(String externalIdFieldName, sObject[] sObjects);

Important point - 

Note: I have written a helper class ConnectionHelper.java which I am using to connect to Salesforce using Enterprise WSDL. To know more about that, please refer my previous post @ 
Below is the Java program - UpsertOperation.java
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.Error;
import com.sforce.soap.enterprise.UpsertResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.SObject;
import com.sforce.ws.ConnectionException;


public class UpsertOperation {
    private EnterpriseConnection enterpriseConnection;
    
    private UpsertOperation(){
        setEnterpriseConnection(ConnectionHelper.doConnect());
    }
    
    public static void main(String[] args) {
        UpsertOperation upsertOperation = new UpsertOperation();
        upsertOperation.doUpsertOperation();
    }
    
    private void doUpsertOperation(){
        
        SObject[] allUpserts = new Account[2];
        
        //Create an Account - Let's say this is a complete new account
        Account account1 = new Account();
        account1.setName("New Account");
        account1.setAccountNumber("11111");
        account1.setRating("Hot");
        account1.setPhone("888888");
        account1.setAccount_External_ID__c("11111");
        
        //Create an Account - Let's say this is an already present account
        Account account2 = new Account();
        account2.setName("United Oil & Gas Corp");
        account2.setAccountNumber("MN355118");
        account2.setRating("Hot");
        account2.setPhone("888888");
        account2.setAccount_External_ID__c("CD355118");
        
        allUpserts[0] = account1;
        allUpserts[1] = account2;
        
        try{
            //Here the comparison will happen based on the field "Account_External_ID__c"
            //This field will determine whether the record is already there is the system or not
            //If the record is already there, then it will do update operation, otherwise
            //it will do insert operation
            UpsertResult[] results = getEnterpriseConnection().upsert("Account_External_ID__c", allUpserts);
            
            for(UpsertResult eachResult : results){
                if(eachResult.isSuccess()){
                    System.out.println("Upsert successful");
                    System.out.println((eachResult.isCreated() ? "Insert" : "Update") + " was performed!!");
                }else{
                    for(Error eachError : eachResult.getErrors()){
                        System.out.println("Error occured with error message: " + eachError.getMessage());
                    }
                }
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }

    public EnterpriseConnection getEnterpriseConnection() {
        return enterpriseConnection;
    }

    public void setEnterpriseConnection(EnterpriseConnection enterpriseConnection) {
        this.enterpriseConnection = enterpriseConnection;
    }

}

The output should look like -
Upsert successful
Insert was performed!!
Upsert successful
Update was performed!!

It's really easy, right!!! If you have any question/feedback, please provide your comment.

In my next post, I will write about delete() operation. Till then enjoy.
Share:

Salesforce Integration with SOAP API - Part 8 - Update Account records with Java program

Preface - This post is part of "Salesforce Integration with SOAP API" series.

In today's post, we will go through the code to understand how we can update records using SOAP API and Java program. Before going into the Java program, I would request you guys to check the documentation @
Update core call with SOAP API
Note: I have written a helper class ConnectionHelper.java which I am using to connect to Salesforce using Enterprise WSDL. To know more about that, please refer my previous post @ 
Now below code will update Account record - 
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.Error;
import com.sforce.soap.enterprise.SaveResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.ws.ConnectionException;


public class UpdateAccount {
    private EnterpriseConnection enterpriseConnection;
    
    private UpdateAccount(){
        setEnterpriseConnection(ConnectionHelper.doConnect());
    }
    
    public static void main(String[] args){
        UpdateAccount updateAccount = new UpdateAccount();
        //Here you need to provide Account ID here
        updateAccount.doUpdateAccount(new String[]{"0019000001ByhBI","0019000000tvEQo"});
    }
    
    private void doUpdateAccount(String[] ids){
        Account[] toBeUpdatedAccount = new Account[ids.length];
        
        for(int i = 0 ; i < ids.length ; i++){
            Account tempAccount = new Account();
            tempAccount.setId(ids[i]);
            tempAccount.setRating("Hot");
            toBeUpdatedAccount[i] = tempAccount;
        }
        
        //Update the account
        try{
            SaveResult[] results = getEnterpriseConnection().update(toBeUpdatedAccount);
            for(SaveResult eachResult : results){
                if(eachResult.isSuccess()){
                    System.out.println("Account updated with ID: " + eachResult.getId());
                }else{
                    for(Error eachError : eachResult.getErrors()){
                        System.out.println("Error message is: " + eachError.getMessage());
                    }
                }
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }

    public EnterpriseConnection getEnterpriseConnection() {
        return enterpriseConnection;
    }

    public void setEnterpriseConnection(EnterpriseConnection enterpriseConnection) {
        this.enterpriseConnection = enterpriseConnection;
    }
}


Now in the below post, I would like to add an existing account to an existing opportunity. I can do that with update() method by passing the opportunity ID and the External ID value of the account record. Important: Keep in mind you need to pass the External ID value of the account record. In my Org, I have setup a custom field Account_External_ID__c and marked that as External ID.
Below code will help you to understand how you can achieve the above requirement -
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.Error;
import com.sforce.soap.enterprise.SaveResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.Opportunity;
import com.sforce.soap.enterprise.sobject.SObject;
import com.sforce.ws.ConnectionException;


public class UpdateOpportunityAccount {
    private EnterpriseConnection enterpriseConnection;
    
    private UpdateOpportunityAccount(){
        setEnterpriseConnection(ConnectionHelper.doConnect());
    }
    
    public static void main(String[] args) {
        UpdateOpportunityAccount updateOpportunityAccount = new UpdateOpportunityAccount();
        updateOpportunityAccount.doUpdateOpportunityAccount("0069000000ReDgi","EXID1111");
    }
    
    private void doUpdateOpportunityAccount(String opportunityId, String accountExternalId){
        try{
            //Link an existing opportunity
            Opportunity toBeUpdatedOpportunity = new Opportunity();
            toBeUpdatedOpportunity.setId(opportunityId);
            
            //Link an existing account
            Account toBeLinkedAccount = new Account();
            toBeLinkedAccount.setAccount_External_ID__c(accountExternalId);
            
            //Link Opportunity with Account
            toBeUpdatedOpportunity.setAccount(toBeLinkedAccount);
            
            SaveResult[] results = getEnterpriseConnection().update(new SObject[]{toBeUpdatedOpportunity});
            for(SaveResult eachResult : results){
                if(eachResult.isSuccess()){
                    System.out.println("Created succesfully with ID: " + eachResult.getId());
                }else{
                    for(Error eachError : eachResult.getErrors()){
                        System.out.println("Error message is: " + eachError.getMessage());
                    }
                }
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }
    
    public EnterpriseConnection getEnterpriseConnection() {
        return enterpriseConnection;
    }
    
    public void setEnterpriseConnection(EnterpriseConnection enterpriseConnection) {
        this.enterpriseConnection = enterpriseConnection;
    }

}


Appreciate your feedback. Thanks.

In my next post, I will explain the upsert operation. Till then keep learning Salesforce.
Share:

Salesforce Integration with SOAP API - Part 7 - Create Account records with Java program

Preface - This post is part of "Salesforce Integration with SOAP API" series.

As of now we have understood how we can query one or more record from Salesforce using query() and queryMore() methods. The posts are @
But what about creating records –

Create() method is used to create one or more records in the Salesforce Org. This is basically doing the INSERT operation. The basic steps are given below –
  • Create an array of sObject[] where each sObject is basically the record you want to create. You need to populate all the required fields.
  • Call create(), passing the sObject[] array
  • Process the results in the SaveResult[] object.
We should understand the below important points before going into the coding –
  • API generates the unique ID fields automatically. As a developer, we can’t explicitly set the value of ID
  • There are few other fields also getting generated automatically and we can’t set them explicitly. The fields are CreatedDate, CreatedById, LastModifiedDate, LastModifiedById and SystemModstamp.
  • For all required fields, if there is no default values, as a developer you need to provide the values.
  • For all non-required fields, if you don’t provide any values, API will put null.
  • As a developer, you need to respect the referential integrity. For example – while creating the child record you need to supply the foreign key information that will link child with parent.
  • While creating Account, Case or Lead, developer can make sure newly created Cases or Leads can be assigned to one or more users as per the assignment rule configured. You need to check AssignmentRuleHeader option.
  • Your client application can create maximum 200 records in a single call of create(). If the create request exceeds 200 records, then the entire operation will fail.
  • The AllOrNoneHeader allows you to define the roll back behavior. The default behavior is to allow successful records to be committed while marking records with error as failed.
  • As a developer, you can create multiple different types of object in a single create() call. You can create upto 10 different object type records in a single call. Records of different object types are broken into chunks and committed chunk wise. If there is any trigger on that object, that trigger will be called once per chunk. Each call can process upto 10 chunks.
  • Records are always saved in the same order that they are entered in the sObject[] array. So if you are entering two object records having parent-child relationship, you need to make sure parent is added first followed by child in the sObject[] array.
  • You can’t add a record that references another record of same object type in the same call. It’s like self – reference. If an account is having parent account, then you can’t insert account and parent account in the same call.
Note: I have written a helper class ConnectionHelper.java which I am using to connect to Salesforce using Enterprise WSDL. To know more about that, please refer my previous post @ 
Now below code will create two Account records - 
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.SaveResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.SObject;
import com.sforce.ws.ConnectionException;
import com.sforce.soap.enterprise.Error;

public class CreateAccount {
    private EnterpriseConnection connection;
    
    private CreateAccount(){
        setConnection(ConnectionHelper.doConnect());
    }
    
    public static void main(String[] args) {
        CreateAccount createAccount = new CreateAccount();
        createAccount.doCreateAccount();
    }
    
    private void doCreateAccount(){
        
        Account account1 = new Account();
        account1.setName("Test Account 1");
        account1.setAccountNumber("11111");
        account1.setRating("Hot");
        account1.setPhone("888888");
        
        Account account2 = new Account();
        account2.setName("Test Account 2");
        account2.setAccountNumber("11111");
        account2.setRating("Hot");
        account2.setPhone("888888");
        
        try{
            SaveResult[] results = getConnection().create(new SObject[]{account1, account2});
            
            //Check the result
            for(SaveResult result : results){
                if(result.isSuccess()){
                    System.out.println("Account created with ID: " + result.getId());
                }else{
                    for(Error anError : result.getErrors()){
                        System.out.println("Error occurred: " + anError.getMessage());
                    }
                }
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }

    public EnterpriseConnection getConnection() {
        return connection;
    }

    public void setConnection(EnterpriseConnection connection) {
        this.connection = connection;
    }

}


We can create parent - child object relationship also using java program. Before that please find what Salesforce documented -

Now let's check the code below -
import java.util.Calendar;

import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.Error;
import com.sforce.soap.enterprise.SaveResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.Opportunity;
import com.sforce.soap.enterprise.sobject.SObject;
import com.sforce.ws.ConnectionException;


public class CreateOpportunityWithAccount {

    private EnterpriseConnection connection;
    private CreateOpportunityWithAccount(){
        setConnection(ConnectionHelper.doConnect());
    }
    
    public static void main(String[] args) {
        CreateOpportunityWithAccount createOpportunityWithAccount = new CreateOpportunityWithAccount();
        createOpportunityWithAccount.doCreateOpportunityWithAccount();
    }
    
    private void doCreateOpportunityWithAccount(){
        try{
            Opportunity anOpportunity = new Opportunity();
            anOpportunity.setName("TEST OPPORTUNITY");
            anOpportunity.setStageName("Prospecting");
            Calendar date = getConnection().getServerTimestamp().getTimestamp();
            date.add(Calendar.DAY_OF_MONTH, 7);
            anOpportunity.setCloseDate(date);
            
            //Create the parent reference. This will be used only for foreign key reference and this should not contain any other field.
            Account refAccount = new Account();
            refAccount.setAccount_External_ID__c("TEST00001");
            anOpportunity.setAccount(refAccount);
            
            //Create the account object. This will be used to create the account
            Account parentAccount = new Account();
            parentAccount.setName("TEST PARENT ACCOUNT");
            parentAccount.setAccount_External_ID__c("TEST00001");
            
            //Create both Account and Opportunity
            //Here the order is very important. parentAccount should be created first followed by anOpportunity, 
            //otherwise the creation of opportunity will fail due to foreign key, but
            //Account will still be created
            SaveResult[] results = getConnection().create(new SObject[]{parentAccount, anOpportunity});
            
            
            //Check the results
            for(SaveResult aResult : results){
                if (aResult.isSuccess()){
                    System.out.println("Successfully created with ID: " + aResult.getId());
                }else{
                    for(Error anError : aResult.getErrors()){
                        System.out.println("Error message: " + anError.getMessage());
                    }
                }
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }

    public EnterpriseConnection getConnection() {
        return connection;
    }

    public void setConnection(EnterpriseConnection connection) {
        this.connection = connection;
    }

}


Hope this will help you to understand the process. Appreciate your feedback.

Wishing you a nice weekend. Cheers
Share:

Salesforce Integration with SOAP API - Part 6 - Bulkify your code - Query more Account records with Java program


Preface - This post is part of "Salesforce Integration with SOAP API" series.

In my previous post, I have explained how to query Account record. The post is @

Salesforce Integration with SOAP API - Part 5 - Query Account Record with Java program

But say for example, if you have huge amount of record you need to fetch from Salesforce using SOAP API, then what you should do? Basically how you can bulkify your code?

Below is what Salesforce documented -



Please have a look what Salesforce tells about QueryLocator. Before going to the code, you need to understand the importance of QueryLocator.











Note: I have written a helper class ConnectionHelper.java which I am using to connect to Salesforce using Enterprise WSDL. To know more about that, please refer my previous post @ 
Now below is the Java code -

import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.QueryResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.soap.enterprise.sobject.SObject;
import com.sforce.ws.ConnectionException;

public class QueryAllAccounts {
    private EnterpriseConnection connection;

    private QueryAllAccounts() {
        setConnection(ConnectionHelper.doConnect());
    }

    public static void main(String[] args) {
        QueryAllAccounts queryAllAccounts = new QueryAllAccounts();
        queryAllAccounts.doQuery();
    }

    private void doQuery(){
        QueryResult queryResult = new QueryResult();
        try{
            queryResult = getConnection().query("SELECT Id,Name,Phone,Rating FROM Account");
            
            boolean done = false;
            if(queryResult.getSize() > 0){
                System.out.println("Found " + queryResult.getSize() + " records.");
                while(!done){
                    for(SObject sObject : queryResult.getRecords()){
                        Account anAccount = (Account)sObject;
                        System.out.println("Account ID: " + anAccount.getId() + " Account Name: " + anAccount.getName() + " Phone: " + anAccount.getPhone() + " Rating: " + anAccount.getRating());
                    }
                    if(queryResult.isDone()){
                        done = true;
                    }else{
                        queryResult = getConnection().queryMore(queryResult.getQueryLocator());
                    }
                }
            }else{
                System.out.println("No record found");
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }

    public EnterpriseConnection getConnection() {
        return connection;
    }

    public void setConnection(EnterpriseConnection connection) {
        this.connection = connection;
    }

}

I have marked the important line in the above code snippet. Hope this post and code snippet will help you to understand the concept. Please provide your feedback.

In my next post, I will explain how you can create records using SOAP API. Till then enjoy learning Salesforce.

Share:

Salesforce Integration with SOAP API - Part 5 - Query Account Record with Java program

Preface - This post is part of "Salesforce Integration with SOAP API" series.

In my previous post, I have explained how can you configure your Eclipse IDE so that you can start writing you own Java program for connecting to Salesforce using SOAP API and also the Java program to check the connectivity. If you are not familiar, I would request you to go through my previous posts. The links are given below -
From this post, I will try to become more technical and concentrate on CRUD (Create, Read, Update and Delete) operation using Java Program and Salesforce's SOAP API methods.

This is the first post where I will explain how you can query Salesforce records using Java program and SOAP API. The below program will fetch a specific Account record and display the information in console -

Note: I have written a helper class ConnectionHelper.java which I am using to connect to Salesforce using Enterprise WSDL. To know more about that, please refer my previous post @ 
Below is the java program which will fetch details of the account named "Burlington Textiles Corp of America". 
FindAccount.java
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.soap.enterprise.QueryResult;
import com.sforce.soap.enterprise.sobject.Account;
import com.sforce.ws.ConnectionException;

public class FindAccount {
    private EnterpriseConnection connection;

    private FindAccount() {
        setConnection(ConnectionHelper.doConnect());
    }

    public static void main(String[] args) {
        FindAccount findAccount = new FindAccount();
        findAccount.doFind();
    }

    private void doFind(){
        QueryResult result = new QueryResult();
        try{
            result = getConnection().query("SELECT Id,Name,Phone,Rating FROM Account WHERE Name = 'Burlington Textiles Corp of America'");
            if(result.getSize() > 0){
                System.out.println("Found " + result.getSize() + " records.");
                Account anAccount = (Account)result.getRecords()[0];
                System.out.println("ID: " + anAccount.getId() + " Name: " + anAccount.getName() + " Phone: " + anAccount.getPhone() + " Rating: " + anAccount.getRating());
            }
        }catch(ConnectionException e){
            e.printStackTrace();
        }
    }

    public EnterpriseConnection getConnection() {
        return connection;
    }

    public void setConnection(EnterpriseConnection connection) {
        this.connection = connection;
    }

}

Explanation: 
Below is what Salesforce's developer guide tells us regarding query() method:

I hope this post will help you to understand how you can fetch a single record using query() method. In my next post, I will explain how you can handle huge amount of data while doing the query. Till then good bye and keep learning.

Any feedback, please let me know. Thanks.

Share:

Salesforce Integration with SOAP API - Part 4 - Java program for connecting to Salesforce using SOAP API and Enterprise WSDL


Preface - This post is part of "Salesforce Integration with SOAP API" series.

In my last post, I have explained how you can configure your Eclipse so that you can start writing your first Java Program to connect to Salesforce using SOAP API. Below is the link of that post -

Salesforce Integration with SOAP API - Part 3 - Configure Eclipse for Enterprise WSDL

Here comes the Java Helper Class to connect to Salesforce using SOAP API.
ConnectionHelper.java:
import com.sforce.soap.enterprise.Connector;
import com.sforce.soap.enterprise.EnterpriseConnection;
import com.sforce.ws.ConnectionException;
import com.sforce.ws.ConnectorConfig;


public class ConnectionHelper {
    static final String USERNAME = "<SALESFORCE USERNAME>";
    static final String PASSWORD = "<SALESFORCE PASSWORD><SECURITY TOKEN>";
    
    public static EnterpriseConnection doConnect(){
        EnterpriseConnection connection = null;
        ConnectorConfig config = new ConnectorConfig();
        config.setUsername(USERNAME);
        config.setPassword(PASSWORD);
        //Add proxy details if requoired
        config.setProxy("<PROXY URL>",<PROXY PORT>);
        config.setProxyPassword("<PROXY PASSWORD>");
        
        try{
            connection = Connector.newConnection(config);
            
            System.out.println("Auth EndPoint: "+config.getAuthEndpoint());
            System.out.println("Service EndPoint: "+config.getServiceEndpoint());
            System.out.println("Username: "+config.getUsername());
            System.out.println("SessionId: "+config.getSessionId());
        }catch (ConnectionException e1) {
            e1.printStackTrace();
        }
        
        return connection;
    }
}

Now I will write the main method to call the doConnect method for connecting to Salesforce. The code is given below -
CheckConnectivity.java
public class CheckConnectivity {
    public static void main(String[] args) {
        ConnectionHelper.doConnect();
    }

}

Output -
You should see an output like this below -
Auth EndPoint: https://login.salesforce.com/services/Soap/c/32.0/0DF900000004uQh
Service EndPoint: https://ap1.salesforce.com/services/Soap/c/32.0/00D90000000s4Hg/0DF900000004uQh
Username: <YOUR USER NAME>
SessionId: 00D90000000s4Hg!ARkAQNCqDG9j80Xm52KDgbN0aZSuQLP0Ql1q0XedDIZ8jymlHtG3Y2KvIEJ04A0KJqPBgqhVzcNvQZ.Wy4k7ugk_Wt0RKACx

That's all. From my next posts, I will start explaining all the CRUD operations using SOAP API. .
Till then any feedback, please let me know. Thanks.

Share:

Salesforce Integration with SOAP API - Part 3 - Configure Eclipse for Enterprise WSDL

Preface - This post is part of "Salesforce Integration with SOAP API" series.

In my previous posts, I have explained basics of SOAP API and WSDL. If you are not familiar with these concepts, I would request you to please check the posts @
Salesforce Integration with SOAP API - Part 1 - Introduction to SOAP Protocol
Salesforce Integration with SOAP API - Part 2 - Introduction to WSDL and Salesforce's Approach

Now here I will show step by step approach to configure eclipse IDE with Enterprise WSDL.

Step 1 -
Download WSDL from Salesforce Org by going to the below links -
Setup | Build | Develop | API and then click on Generate Enterprise WSDL, followed by click on Generate button. It will open an XML file. You need to save the XML in your hard drive as enterprise.wsdl

Step 2 -
Here you need to download the Force.com Web Service Connector (WSC) from WSC project's download page.
So download wsc-23.jar from the above location and save it to the same folder where you have saved enterprise.wsdl in the previous step.

Step 3 -
In this step you need to create the executable jar. The command is as below -
java -classpath wsc-23.jar com.sforce.ws.tools.wsdlc enterprise.wsdl enterprise.jar
This will create an executable jar named enterprise.jar  in the same folder. Screenshot attached.

Step 4 -
Now you can create java project in Eclipse IDE and add the below jars to the build path -
  • wsc-23.jar
  • enterprise.jar
That's all you need to do to configure your Eclipse IDE. Now in the next post, I will dive into the coding part to connect to Salesforce using SOAP API. I will use Java as programming language here. 

Any feedback, please let me know. Thanks.
Share:

Salesforce Integration - Creating XML Request using DOM + Custom Settings


Recently I got a requirement from one of my customer where I need to create the XML request and pass the same to a third party application using SOAP protocol. Initially I though it is very easy to do with Apex DOM APIs, but then I came to know that the other party (developer from that Third Party application) change their application quite frequently and as a result they need XML response in a different structure everytime they made some changes. Definitely my customer was very much irritated with this as they need to do code changes every time the other party do the change. So the important part of the requirement was to design something or do something so that with minimum (preferably no code change) changes Salesforce admin can handle the request from other party.

Definitely quite a good challenge. Then after some discussion with the other party and Salesforce admin, I designed the application. The most complex part was how to create the XML Request dynamically(without any code change from Salesforce side) with new XML structure requirement.

In this post, I will explain how I implemented the logic of creating XML Request with the help of Custom Settings. Yes, you are correct. Custom Settings is so powerful and important that you can play with it in many different ways and this post is one of the way.

Let's get started. Below is the XML request format for an Account record -

Now below are the Custom Settings I have created -
Custom Settings: SD Namespaces
Description: This custom settings will holds all the namespaces required in the request XML. If the other party adds a new namespace, Salesforce admin can always add the new namespace. No code change is required for addition of new namespaces or modification of existing one or deletion of new one.

Custom Settings: SD Service Fields
Description: This custom settings stores the details and structure of messages used in the SD Integration process. The field names are quite self explanatory. This custom settings is very important and later in the post, I will show how a simple change in this custom settings can completely change the XML Request structure.


Now let's write down the apex code -
public class RequestController {
    
    public void generateXmlRequest(){
        DOM.Document body = new DOM.Document();
        String prefix = 'SD';
        String nameSpace = selectedOperation;
        String method = 'createAccountRequest';
        
        DOM.XmlNode root = body.createRootElement('topResponseTag', nameSpace, prefix);
        
        List<String> allRequieredFields = fetchAccountFields();
        String requiredFields = String.join(allRequieredFields, ',');
        
        List<Account> allAccounts = Database.query('SELECT ' + requiredFields + ', (select id, name, email from Contacts) FROM ACCOUNT');
        
        List<SD_Service_Fields__c> serviceFields = [
                SELECT Name, Request_Name__c, Sequence__c, Namespace_Prefix__c, Parent_Node__c, Type__c, XmlLabel__c, Source_Object__c, Field_Name__c
                FROM SD_Service_Fields__c 
                WHERE Request_Name__c = :method
                ORDER BY Sequence__c];
        System.Debug('Found: ' + serviceFields.size());
        
        //Add Account Details one by one
        for(Account eachAccount : allAccounts){
            Dom.XmlNode accountNode = root.addChildElement(method, nameSpace, prefix);
            appendSObjectToXml(accountNode, eachAccount, serviceFields);
        }
    }
    
    private List<String> fetchAccountFields(){
        List<SD_Service_Fields__c> allServiceFields = [
                SELECT Name, Field_Name__c FROM SD_Service_Fields__c 
                WHERE Type__c = 'Field' and Source_Object__c = 'Account'];
        List<String> allFields = new List<String>();
        for(SD_Service_Fields__c aServiceField : allServiceFields){
            allFields.add(aServiceField.Field_Name__c);
        }
        return allFields;
    }
    
    private void appendSObjectToXml(Dom.XmlNode accountNode, Account eachAccount, List<SD_Service_Fields__c> serviceFields){
        Map<string, dom.xmlnode> xmlnodemap = new Map<string, dom.xmlnode>();
        //get Namespaces from custom setting
        Map<String,SD_Namespaces__c> allNameSpaces = SD_Namespaces__c.getall();
            
        for(SD_Service_Fields__c field : serviceFields){
            if(field.Type__c == 'XmlNode'){
                if(field.Parent_Node__c == 'root'){
                    xmlnodemap.put(field.XmlLabel__c, accountNode);
                }else{
                    xmlnodemap.put(field.XmlLabel__c, 
                                    xmlnodemap.get(field.parent_Node__c).addChildElement(field.XmlLabel__c, allNameSpaces.get(field.Namespace_Prefix__c).namespace__c, field.Namespace_Prefix__c));
                }
            }else if(field.Type__c == 'Field'){
                String innerText = '';
                Boolean isNull = true;
                
                //get the required field 
                String fieldRequired = field.Field_Name__c;
                sObject anAccount = eachAccount;
                Object value = anAccount.get(fieldRequired);
                System.Debug('Field: ' + fieldRequired + ' Value: ' + String.valueOf(value));
                
                if(value != null){
                    innerText = String.valueOf(value);
                    isNull = false;
                }
                
                if(!isNull){
                    DOM.XmlNode xmlField = xmlnodemap.get(field.parent_Node__c).addChildElement(field.xmlLabel__c, allNameSpaces.get(field.Namespace_Prefix__c).namespace__c, field.Namespace_Prefix__c);                                
                    xmlField.addTextNode(innerText); 
                }
            }else if(field.Type__c == 'Constant'){
                
            }
        }
    }
    
    public List<SelectOption> getPossibleOperations(){
        List<SelectOption> possibleOperations = new List<SelectOption>();
        
        //Fetch all namespaces available from Custom Settings
        Map<String,SD_Namespaces__c> allNameSpaces = SD_Namespaces__c.getall();
        
        //Create the select option
        for(String operationName: allNameSpaces.keySet()){
            possibleOperations.add(new SelectOption(allNameSpaces.get(operationName).Namespace__c, allNameSpaces.get(operationName).Name));
        }
        
        return possibleOperations;
    }
}

The above code will help to create the XML Request complete based on the Custom Settings.
The generated XML Request snippet is given below -

Now let's see what we need to do if we want to change the XML Structure

Let's say now we have to make the below changes in the XML request structure  -
  • Pass "Name" field information instead of "AccountNumber" in the first child one.
  • Pass the below account details in sequence under objectDetails XmlNode -
    • Phone
    • Fax
    • Type
    • Rating
In order to do the above changes, we need to do changes only in Custom Settings - "SD Service Fields" as shown below - 
The generated XML is shown below -


Great!!. My customer is very happy with this approach. Appreciate your feedback. Thanks,
Share:

Salesforce Integration with SOAP API - Part 2 - Introduction to WSDL and Salesforce's Approach


Preface - This post is part of "Salesforce Integration with SOAP API" series.

Introduction to WSDL –
WSDL (Web Service Descriptive Language) is an XML format for describing network services as a set of endpoints operating on messages containing either document-oriented or procedure-oriented information.
Salesforce provides WSDL documents that describe –
  • The public interface to the Web service.
  • The protocol to be used for exchanging information (SOAP).

In Salesforce we have two different types of WSDL documents –
  • Enterprise WSDL – Strongly typed representation of an organization’s data.
  • Partner WSDL – Loosely typed representation of an organization’s data.

We should understand the differences between above two WSDLs. The differences are listed below –
Enterprise WSDL –
  • This is strongly typed.
  • This is tied (bound) to a specific configuration of Salesforce.
  • For any changes (Addition or Deletion of Custom Objects/Custom Fields) WSDL should be copied from the organization again.
  • The enterprise WSDL is intended primarily for Customers.
Partner WSDL –
  • This is loosely typed.
  • This can be used to reflect against any configuration of Salesforce.
  • Partner WSDL is static. So if any changes are made to an organization’s Salesforce, partner WSDL should not be changed.
  • The Partner WSDL is intended primarily for Partners.
In my next post, I will explain how to configure eclipse IDE to connect to Salesforce using Java programming language. If you have any feedback, please let me know. Thanks.
Share:

Salesforce Integration with SOAP API - Part 1 - Introduction to SOAP Protocol

Preface - This post is part of "Salesforce Integration with SOAP API" series.

Introduction to SOAP

SOAP (Simple Object Access Protocol) is a network application messaging protocol used to exchange structured information between systems. It uses XML as message format and relies on application layer protocol, mainly HTTP, SMTP. It allows programs to run on separate machines and communicate using HTTP and it’s XML. To know more about SOAP, please visit below links:

Introduction to SOAP API
SOAP API is a mechanism that allows external system to connect to Salesforce so that they can do CRUD operation, perform search operation, as well as perform administrative tasks.

This SOAP API will expose the below details to external system –
  • Standard Salesforce objects including all custom fields.
  • All Custom Salesforce objects and all fields.
  • Object Metadata.
Using SOAP API, user can do below things –
  • Perform CRUD (Create, Read, Update, Delete) operations on data.
  • Query Salesforce data.
  • Work with approval process.
  • Create and update sharing records. 

As a best practice, developer should use the API options for creating Share records only during migration. Otherwise developer should always keep business rules out of Integration layer.
Share:

Spring '15 - New Feature Added - Access Address and Geolocation Compound Fields Using Apex


Another feature added in Sprint '15 is the ability to access Address and Geolocation Compound Field using Apex. I will explain the new feature with example below -

First I will create a new field of type Geolocation in the User object as shown below -

Now I will write the below code to access Location and Address details from User as below -
public class UserLocation {
    public List<Double> getUserGeoLocation(String userName){
        User anUser = [Select Id, Location__c from User where Username = :userName LIMIT 1];
        Location location = anUser.Location__c;
        List<Double> geoLocation = new List<Double>();
        geoLocation.add(location.latitude);
        geoLocation.add(location.longitude);
        return geoLocation;
    }
    
    public Address getUserAddress(String userName){
        User anUser = [Select Id, Address from User where Username = :userName LIMIT 1];
        return anUser.Address;
    }
}

I will write the test class also to test the functionality as shown below -
@isTest
public class UserLocationTest {
    @testSetup static void setup() {
        User userToCreate = new User();        
        //Add required information     
        userToCreate.FirstName = 'Sudipta';    
        userToCreate.LastName = 'Deb';    
        userToCreate.Alias = 'sdeb';    
        userToCreate.Email = 'sudipta.deb@gmail.com';    
        userToCreate.Username = 'sudipta-deb@gmail.com';    
        userToCreate.CommunityNickname = 'Sudipta.Deb';    
        userToCreate.ProfileId = '00eB0000000F8dB';      
        userToCreate.TimeZoneSidKey    = 'America/Denver';         
        userToCreate.LocaleSidKey      = 'en_US';         
        userToCreate.EmailEncodingKey  = 'UTF-8';         
        userToCreate.LanguageLocaleKey = 'en_US';
        //Adding the information Geolocation
        userToCreate.Location__Latitude__s = 22.5667;
        userToCreate.Location__Longitude__s = 88.3667;
        //Adding the Address information
        userToCreate.State = 'West Bengal';
        userToCreate.City = 'Kolkata';
        userToCreate.PostalCode = '12345';
        userToCreate.Country = 'India';
        
        insert userToCreate;
    }
    
    static testMethod void testGetUserGeoLocation(){
        UserLocation userLocation = new UserLocation();
        Test.startTest();
        List<Double> userGeoLocation = userLocation.getUserGeoLocation('sudipta-deb@gmail.com');
        Test.stopTest();
        
        System.assertEquals(22.5667, userGeoLocation.get(0));
        System.assertEquals(88.3667, userGeoLocation.get(1));
    }
    
    static testMethod void testGetUserAddress(){
        UserLocation userLocation = new UserLocation();
        Test.startTest();
        Address userAddress = userLocation.getUserAddress('sudipta-deb@gmail.com');
        Test.stopTest();
        
        System.assertEquals('West Bengal', userAddress.State);
        System.assertEquals('Kolkata', userAddress.City);
        System.assertEquals('12345', userAddress.PostalCode);
        System.assertEquals('India', userAddress.Country);
    }
}

You will notice that the test method created above is using another new feature introduced in Spring '15 i.e. @testSetup. I have written a post on that. Please check Spring '15 - New Feature Added - @testSetup

This is really a great feature introduced. You as a developer now have huge power to do many things for example - calculating distance between two geolocations (I will write a post on this very soon).

You should read this great resource before start working with Compound fields. 

You can try this new feature in your own Spring '15 Pre-release Org! 

Share:

Spring '15 - New Feature Added - @testSetup

One of the very interesting feature added in Spring '15 is @testSetup. In this post I will try to explain with example more about the feature. 
Below is what you will get from release note about @testSetup.

Now let's check with example -

Below is the class/methods for which we need to write test methods - 
public class AccountHelper {
    public List<Account> getAllAccounts(){
        List<Account> allAccounts = [Select Id from Account];
        return allAccounts;
    }
    
    public Integer getEmployeeCountFrom(String accountNumber){
        Account fetchedAccount = [Select Id, NumberOfEmployees from Account where AccountNumber = :accountNumber LIMIT 1];
        return fetchedAccount.NumberOfEmployees;
    }
    
    public List<Case> getAllCases(Id accountId){
        return [select ID from Case where AccountId = :accountId];
    }
    
    public void updateEmployeeCountFor(String accountNumber, Integer newEmployeeCount){
        Account fetchedAccount = [Select Id, NumberOfEmployees from Account where AccountNumber = :accountNumber LIMIT 1];
        fetchedAccount.NumberOfEmployees = newEmployeeCount;
        update fetchedAccount;
    }
}

Previously we used to write test classes like below -
@isTest
public class AccountHelperTest {
    static testMethod void testGetAllAccounts(){
        List<Account> accounts = new List<Account>();
        for(Integer i=0;i < 100;i++){
            accounts.add(new Account(Name = 'Universal Container'));
        }
        insert accounts;
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Account> allAccounts = accountHelper.getAllAccounts();
        Test.stopTest();
        
        System.assertEquals(100, allAccounts.size());
    }
    
    static testMethod void testGetEmployeeCountFrom(){
        Account anAccount = new Account();
        anAccount.Name = 'Universal Container';
        anAccount.AccountNumber = 'TEST';
        anAccount.NumberOfEmployees = 100;
        
        insert anAccount;
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(100, numberOfEmployees);
    }
    
    static testMethod void testGetAllCases(){
        //Create the account first
        Account anAccount = new Account();
        anAccount.Name = 'Universal Container';
        insert anAccount;
        
        //Create the cases now
        List<Case> cases = new List<Case>();
        for(Integer i=0;i < 10;i++){
            cases.add(new Case(
                Status = 'New',
                Priority = 'Medium',
                AccountId = anAccount.Id,
                Origin = 'Phone'
            ));
        }
        insert cases;
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Case> allCases = accountHelper.getAllCases(anAccount.id);
        Test.stopTest();
        
        System.assertEquals(10, allCases.size());
    }
}

The disadvantages with this approach is that for every test method we need to write block of codes to setup data. Why not create test data for all the test methods in a test class once and use them everywhere. More organised, right? Exactly that is what we can do now with methods annotated with @testSetup. The method annotated with @testSetup will be responsible for setting up test data at the very beginning before executing the first test method. When all the test methods are executed successfully, the test data will be roll backed automatically. Another advantage with this approach is that if any test method modifies the test data, the modification will be roll backed after executing that particular test method. So the next test method will get the original test data being setup at the beginning.

Let's see how we can write test methods using new approach -
@isTest
public class ApexHelperTestAdvanced {
    @testSetup static void setup() {
        // Create common test accounts
        List<Account> accounts = new List<Account>();
        for(Integer i=0;i < 100;i++){
            accounts.add(new Account(Name = 'Universal Container'));
        }
        
        //Add few account details to one Account
        accounts[0].AccountNumber = 'TEST';
        accounts[0].NumberOfEmployees = 100;
        
        //Insert Account
        insert accounts;
        
        //Add Case Details
        List<Case> cases = new List<Case>();
        for(Integer i=0;i < 10;i++){
            cases.add(new Case(
                Status = 'New',
                Priority = 'Medium',
                AccountId = accounts[0].Id,
                Origin = 'Phone'
            ));
        }
        
        //Insert cases
        insert cases;        
    }
    
    static testMethod void testGetAllAccounts(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Account> allAccounts = accountHelper.getAllAccounts();
        Test.stopTest();
        
        System.assertEquals(100, allAccounts.size());
    }
    
    static testMethod void testGetEmployeeCountFrom(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(100, numberOfEmployees);
    }
    
    static testMethod void testGetAllCases(){
        //Fetch the account ID
        Account fetchedAccount = [Select Id from Account where AccountNumber = 'Test'];
        
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        List<Case> allCases = accountHelper.getAllCases(fetchedAccount.id);
        Test.stopTest();
        
        System.assertEquals(10, allCases.size());
    }
    
    static testMethod void testUpdateEmployeeCountFor(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        accountHelper.updateEmployeeCountFor('TEST',200);
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(200, numberOfEmployees);
    }
    
    static testMethod void testRollBackFunctionality(){
        Test.startTest();
        AccountHelper accountHelper = new AccountHelper();
        Integer numberOfEmployees = accountHelper.getEmployeeCountFrom('TEST');
        Test.stopTest();
        
        System.assertEquals(100, numberOfEmployees);
    }
}

Here as you can see we have a method named setup annotated with @testSetup and this method will setup all the test data. Now from the test methods we are not calling any method or doing something to setup data. It is being setup already by the method "setup" at the beginning. Great, Awesome. So we are moving towards TDD (Test Driven Development) approach

Another point to notice here is that the testMethod name testUpdateEmployeeCountFor is basically modifying the test data by making the number of employees to 200. Now in the next test method i.e. testRollBackFunctionality, we are checking whether the number of employees is again 100 (original values setup earlier). It indicates that any modification in test data by any test method will be rolled back at the end of the execution of that test method. 

Below are few points which we should keep in mind as - 

Try this new feature in your own Spring '15 Pre-release Org! 
Please provide your feedback. Thanks.

Share:

Follow Me

Enter your email address:

Delivered by FeedBurner

Popular Posts

Labels

Salesforce (105) Apex (44) admin (27) ADM (20) visualforce (20) dev 501 (19) integration (18) learn salesforce (18) 501 (16) SOAP (13) lightning (12) tutorial (11) Certification. (9) javascript (8) Trigger (7) test class (7) unit testing (7) Sharing and Visibility (6) design pattern (6) report (6) security (6) trailhead (6) Advanced Admin (5) Certification (5) Kitchener Developer Group (5) New Features (5) SOQL (5) css (5) dashboard (5) debug (5) developer (5) formula (5) mobile (5) salesforce release (5) service cloud (5) solution management (5) use case (5) JSON (4) Lightning Experience (4) Salesforce DX (4) WebSphere (4) best practice (4) cast iron (4) component (4) deployment (4) github (4) html (4) polymer (4) profiles (4) responsive (4) tdd (4) ui (4) Advanced Apex (3) Architect (3) Live Chat (3) Online Event (3) Performance (3) Products (3) Role (3) Sales Cloud (3) Scratch Org (3) Study Notes. (3) Summer15 (3) Tips (3) Web Technology (3) dynamic apex (3) event (3) license (3) map (3) mapbox (3) singleton (3) version controlling (3) Bulkify (2) Data Architecture and Management Certification (2) Devops (2) Distributed Version Controlling (2) ES6 (2) Eclipse (2) Einstein (2) Enterprise Territory Management (2) Financial Services Cloud (2) Force.com IDE (2) Governor Limit (2) Groups (2) IBM (2) Implicit Sharing (2) JourneyToCTA (2) Kitchener User Group (2) Lightning Design System (2) Live Agent (2) Metadata (2) Opportunity (2) Price Book (2) REST (2) SOSL (2) Sharing (2) Spring 15 (2) Summer17 (2) Territory (2) ant (2) automation tool (2) basic (2) chatter (2) coding (2) communication (2) console (2) controller (2) documentation (2) flow (2) git (2) jquery (2) logging (2) object (2) permission (2) process builder (2) release (2) salesforce1 (2) strategy (2) xml (2) Action Plan (1) Action Plan Template (1) Agent Productivity (1) Analytics (1) Apex Sharing (1) Arrow (1) Asynchronous callout (1) Aura Framework (1) Bots (1) Browser (1) Bulk data load (1) CTA (1) Calendar (1) Canon (1) Case Management (1) Cheat Sheet (1) Classic (1) Community (1) Constructor (1) Contact Center (1) Continuation (1) Continuous Integration (1) Convert (1) Cookie (1) Custom Metadata (1) Custom Object (1) Customer (1) Decorator Design Pattern (1) Dev Hub (1) Diwali (1) Email (1) FSC (1) Function (1) Goals (1) Guide (1) Household (1) Ideas (1) Improvement (1) KPIs (1) Large Data Volume (1) LastModifiedDate (1) Lightning Web Component (1) Manual Sharing (1) Metrics (1) New (1) OOPS (1) OWD (1) Omni-Channel (1) Partner (1) Person Account (1) Photo (1) Pipeline (1) Platform Developer I (1) Presentation (1) Product Schedule (1) Profile (1) Promise (1) Prototype (1) Public Site (1) Query Plan (1) QuickReference (1) Reports (1) Retrieve (1) Role Hierarchy (1) SFDX (1) Salesforce Optimizer (1) Session (1) Sharing Rule (1) Sharing Sets (1) Site (1) Skills (1) Snap-ins (1) Spring 17 (1) Summer14 (1) Summer16 (1) Switch (1) SystemModStamp (1) User License (1) Users (1) Webservice (1) Winter'15 (1) Winter'17 (1) access (1) agile (1) app (1) approval process (1) aura (1) awesome (1) backup (1) bitbucket (1) book (1) campaign (1) change set (1) code (1) code coverage (1) configuration (1) csv (1) custom button (1) custom settings (1) customization (1) data loader (1) database (1) delegate Admin (1) describe (1) dom (1) dreamforce (1) duplicate (1) dynamic (1) equals (1) error (1) field-level security (1) folder (1) ftp (1) generic (1) gift (1) global describe (1) hashcode (1) import wizard (1) jenkins (1) keynote (1) long running requests (1) monitoring (1) mysql (1) page layout (1) personal (1) power of one (1) record type (1) relationship (1) request (1) review (1) sub-tab (1) tab (1) username (1) visual workflow (1) workflow (1)

Total Subscribers

Total Pageviews