How to integrate Bizagi with Salesforce

<< Click to Display Table of Contents >>

Navigation:  Bizagi Studio > How To´s > Integration how-to's >

How to integrate Bizagi with Salesforce

Overview

Integrating Bizagi with a CRM such as Salesforce, is supported through more than one alternative.

This section presents an example of how you can present in Bizagi all leads stored in Salesforce, while allowing that same user to create a new lead based on the information already entered in Bizagi:

 

Sforce_Execute1

 

 

 

Alternatives

For such scenarios where you want your Bizagi process to connect to your corporate CRM systems in general, to either retrieve information (e.g, Leads, Accounts, Opportunities or Contacts), or to create new records, you may rely on these different features:

 

1. Bizagi Web services connector

Allows you to connect to SOAP-compliant Web services without the need of programming.

For more information about the Web services connector, refer to Invoking external services from Bizagi.

 

2. Bizagi Connectors

Allows you to use existing connectors from Bizagi Connector Xchange, or create you own connectors.

Once you have a connector for your CRM system, it is registered in Bizagi to be used from the process business rules.

Bizagi Connectors will allow you to rely on powerful built-in options, such as graphical mapping for inputs and outputs, or the use of traces to troubleshoot.

For more information about this feature, refer to Bizagi Connectors.

 

3. Bizagi Component library

Allows you to create you own coded components and bundle them in Bizagi, so that you can extend your business rules by using those components directly.

For more information about Bizagi Component library feature, refer to Custom components.

 

note_pin

Though the preferred option is usually the Web services connector, for this specific how-to which involves Salesforce services, this alternative is not optimal.

Further description illustrating when best to use each feature is described below.

 

Salesforce API

Because Salesforce is offered as a cloud service which has an internet-accessible API, it demands the use of a dynamic session ID to invoke any of its methods (for cloud-safety reasons, in order to restrict the invocation of its services to a time frame in which that session ID is valid).

Salesforce SOAP-based API relies on an authentication endpoint that is separate from the service endpoint, and this implies that the logic behind undergoes an authentication flow similar to that one used by those APIs which are based on OAuth exchanges (https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_login.htm).

 

Therefore, using the Web services connector is not optimal because getting a dynamic session ID means that you need to invoke first the Salesforce login method (via an authentication login, password and security token), to then invoke the method you are looking for.

The Web service connector for this approach would result in having more than one call from Bizagi to Salesforce, while not following best practices regarding the management of transactions or highest security aspects when handling session IDs.

 

Due to the above, the best option to approach this integration scenario is to use Bizagi Connectors, though in this how-to we will be invoking Salesforce via its SOAP API, but through the Component library feature.

For this feature we will develop a custom component that handles the session ID and all due aspects which are specific to this authentication mechanism, as available in Salesforce.

 

Before you start

Before you move on with the integration illustrated by this how-to, make sure you already have a valid account at Salesforce.

 

Sforce_results

 

When doing so, note that you will need to grant access to your machine, so that it is allowed to use your Salesforce account, as required by Salesforce security policies.

You may authorize such access to your Salesforce account and set up an explicit security token, directly at the Personal settings presented in your Salesforce portal (e.g, https://na22.salesforce.com/_ui/system/security/ResetApiTokenEdit?retURL=%2Fui%2Fsetup%2FSetup%3Fsetupid%3DPersonalInfo&setupid=ResetApiToken).

 

Sforce_securitytoken

 

What you need to do

In addition to requisites at Salesforce, the following list illustrates a general outline of steps you need to carry out when integrating to Salesforce:

 

1. Create your own component that connects to Salesforce.

This step is done outside of Bizagi to produce a class library.

 

2. Register the component in Bizagi Studio.

This step means using Bizagi Studio to import the class library output into your Bizagi project.

 

3. Configure a business rule that invokes your component.

This step means configuring the use of methods in that component, so that you may invoke Salesforce from any point of your Bizagi processes.

In order to accomplish these steps, you will need to have included in your process data model and user interfaces, the appropriate definitions to either send out information to Salesforce, or to store incoming one.

 

Example

To illustrate how to integrate with Salesforce, we will part from having already implemented a Petition, claims and complaints process, just as the one available directly in Bizagi's Process Xchange (http://www.bizagi.com/en/community/process-xchange).

The main objective of this process is to assign responsibilities and to follow up the incoming comments that are either a petition, claim, complaint or suggestion, in order to handle them accordingly throughout the organization.

 

Sforce_PCC2

 

For the sake of the example, we will be assuming for incoming requests which are strictly suggestions:

1.The person in charge of analyzing that suggestion ("Case resolver", appointed to the Analyze suggestion task) may choose to create a new lead with the requester's data.

2.Bizagi will present a table with all current leads in Salesforce for the Case resolver to use as real-time reference.

Once the suggestion has been analyzed and assuming that the requester is to be created as a new lead, Bizagi will persist this information in Salesforce.

 

 

All in all, in this how-to we will be executing these 2 integration points with Salesforce by adding configuration to the existing process template in the Send answer and Analyze Suggestion activities.

The Send answer activity will be the integration point where Bizagi fetches all leads from Salesforce (i.e a read), while the Analyze Suggestion activity will be the integration point where Bizagi submits the new lead (i.e a write).

 

note_pin

In order to follow the steps presented in this section, to have a fully running application on your side, you are encouraged to download and install the Petitions, Claims and Complaints process template from the Process Xchange.

For more information about this process template, and how you can immediately download it and use it at no cost, refer to Bizagi Process Xchange.

 

 

 

Steps

The following steps and component are developed as an example targeted to a .NET platform.

 

1. Create your own component that connects to Salesforce.

The first step before even starting to develop the component, is to ensure that you have prepared everything needed to connect to Salesforce.

In addition to having an account and security token as described in the above Before you start section, you will also need to first save the definition of those services made available by the SOAP API of your Salesforce account.

Saving that definition (the actual WSDL) is done into your local machine, and to be hosted by a running IIS web server.

 

note_pin

This how-to is based on the guidelines and example provided by Salesforce itself at https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_steps.htm, though some adjustments were made.

Note that Salesforce may update its documentation, its API support or its recommended practices at anytime without previous notice.

The API used by this example is the version 35.0 (this information presented as comments in the WSDL).

 

For information about Salesforce SOAP API or further documentation about its objects and specifics, refer as well to https://developer.salesforce.com/page/Sample_SOAP_Messages.

 

1.1 Locate the WSDL

Locate the WSDL by logging in to Salesforce portal and then typing API in the quick search box.

Within its search results, you should be able to browse into the Develop API page:

 

Sforce_portal1

 

1.2 Save the WSDL

Once in the API WSDL page, download and save locally the generated WSDL file for your Enterprise Salesforce services, by using your browser's "save link as..." option:

 

Sforce_portal2

 

Notice this file should use the .wsdl file extension (furthermore, the complete default name for this file is "enterprise.wsdl").

 

1.3 Host the file at the IIS

In order to make this file web-accessible, save this file into the local IIS start path at C:\inetpub\wwwroot\.

The image below shows the enterprise.wsdl file being saved into C:\inetpub\e wwwroot\LocalWS\:

 

Sforce_portal3

 

1.4 Modify the WSDL

This step is needed to resolve a known issue which Salesforce has regarding its WSDL definitions.

Modify the WSDL by including this additional line:

<xsd:attribute name="tmp" type="xsd:string" />

into the ListViewRecord element definition.

 

This means changing this original definition:

<complexType name="ListViewRecord">

 <sequence>

           <element name="columns" type="tns:ListViewRecordColumn" maxOccurs="unbounded"/>

 </sequence>

</complexType>

 

Into this one:

<complexType name="ListViewRecord">

 <sequence>

           <element name="columns" type="tns:ListViewRecordColumn" maxOccurs="unbounded"/>

 </sequence>

 <xsd:attribute name="tmp" type="xsd:string" />

</complexType>

 

Sforce_wsdledit

 

1.5 Verify the WSDL from web-access

At this point, you should be able to access this WSDL via the HTTP protocol.

Use http://localhost/[PATH_TO_YOUR_WSDL] from a browser of your choice, to verify that WSDL is web-accessible and that its structure is XML-consistent after having made modifications to it.

 

As specified in previous steps, the image below uses the LocalWS folder and therefore has the WSDL accessible at http://localhost/LocalWS/enterprise.wsdl:

 

Sforce_wsdl

 

1.6 Create a new Visual Studio project.

Now that you have everything needed from Salesforce, proceed to create the component.

To do so, create a new class library project that is targeted for the .NET version 4.0 framework:

 

Sforce_visual1

 

 

1.7 Add a service reference to the WSDL

As described in Salesforce documentation, add a service reference that points to the WSDL hosted at the IIS:

 

Sforce_reference  

 

Notice that the service reference namespace for this example is called sforce2.

Though you may give it any name of your choice, keep into account that the sforce2 exact name will be referenced in further steps in this how-to.

 

1.8 Add other libraries

Include other libraries as needed, especially the System.ServiceModel library:

 

Sforce_import

 

1.9 Adjust buffer parameters

All .NET projects by default use buffer parameters set to a low value, aimed for transmitting small amounts of data.

Ensure you adjust such parameters so that you explicitly allow your application to transmit larger amounts of data (you should adjust this in a measurable fashion for a production environment since these settings are part of security measures that mitigate denial of service attacks).

Because the Salesforce example fetches all leads (though an approximate of 22 records), but alongside with a lot of metadata information, proceed to modify the buffer parameters which are: maxBufferSize, maxReceivedMessageSize and maxBufferPoolSize.

 

To do so, edit the binding element found directly in your project's app.config so that it uses the following values (or as measured accordingly to the expected information you will manage):

 

<binding name="SoapBinding"  maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferPoolSize="524288">

 <security mode="Transport" />

</binding>

 

Sforce_appconfig

 

1.10 Code the component

Include the code for the main Class.cs of your class library project.

You may use the following code which was based on the quick-start Salesforce documentation (https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_steps_walk_through_code.htm):

 

C# codeView code here
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.Xml.Linq;
using MySalesforceLibrary.sforce2;

namespace MySalesforceLibrary
{
   public class Class1
   {
       private static SoapClient loginClient;
       private static SoapClient client;
       private static SessionHeader header;
       private static EndpointAddress endpoint;

       #region public Methods
       #region Tests everything this component supports
       public void Test(String userName, String password, String securityToken, String objectType,
           String firstName, String lastName, String company, String email)
       {
           if (login(userName, password, securityToken))
           {
               describeGlobals(objectType);
               queryLeads();
               createLead(firstName, lastName, company, email);

               logout();
           }
       }
       #endregion

       #region Tests the connection and fetching metadata
       public void Connect(String userName, String password, String securityToken, String objectType)
       {
           if (login(userName, password, securityToken))
           {
               describeGlobals(objectType);
               logout();
           }
       }
       #endregion

       #region Runs a query which fetches all leads
       public String Query(String userName, String password, String securityToken)
       {
           String results = "<ClaimandComplaintRequest>";
           if (login(userName, password, securityToken))
           {
               results += queryLeads();
               logout();
           }
           return results + "</ClaimandComplaintRequest>";
       }
       #endregion

       #region Creates a Lead
       public String Create(String userName, String password, String securityToken,
           String firstName, String lastName, String company, String email)
       {
           String confirmationId = "0";
           if (login(userName, password, securityToken))
           {
               confirmationId = createLead(firstName, lastName, company, email);
               logout();
           }
           return confirmationId;
       }
       #endregion
       #endregion

       #region private Methods
       #region login
       private bool login(String userName, String password, String securityToken)
       {
           // Create a SoapClient specifically for logging in
           loginClient = new SoapClient();
           LoginResult lr = null;

           try
           {
               Console.WriteLine("\nLogging in...\n");
               lr = loginClient.login(null, userName, password + securityToken);
           }
           catch (Exception e)
           {
               Console.WriteLine("An unexpected error has occurred: " + e.Message + "\nStack trace: " + e.StackTrace);
           }

           // Check if the password has expired
           if (lr.passwordExpired)
           {
               Console.WriteLine("An error has occurred. Your password has expired.");
               return false;
           }

           // On successful login, cache session info and API endpoint info
           endpoint = new EndpointAddress(lr.serverUrl);
           header = new SessionHeader();
           header.sessionId = lr.sessionId;

           // Create and cache an API endpoint client
           client = new SoapClient("Soap", endpoint);
           printUserInfo(lr, lr.serverUrl);

           return true;
       }
       #endregion

       #region printLogin
       private void printUserInfo(LoginResult lr, String authEP)
       {
           try
           {
               GetUserInfoResult userInfo = lr.userInfo;
               Console.WriteLine("UserID: " + userInfo.userId);
               Console.WriteLine("User Full Name: " + userInfo.userFullName);
               Console.WriteLine("User Email: " + userInfo.userEmail);
               Console.WriteLine();
               Console.WriteLine("SessionID: " + lr.sessionId);
               Console.WriteLine("Auth End Point: " + lr.serverUrl);
               Console.WriteLine("Service End Point: " + lr.serverUrl);
               Console.WriteLine();
           }
           catch (Exception e)
           {
               Console.WriteLine("An unexpected error has occurred: " + e.Message + "\nStack trace: " + e.StackTrace);
           }
       }
       #endregion

       #region describeGlobals
       private void describeGlobals(String myObjectType)
       {
           #region describeGlobalSample
           try
           {
               // describeGlobal() returns an array of object results that includes the object names that are available to the logged-in user.
               DescribeGlobalResult dgr;
               client.describeGlobal(header, null, out dgr);
               Console.WriteLine("\nDescribe Global Results:\n");

               // Loop through the array echoing the object names
               for (int i = 0; i < dgr.sobjects.Length; i++)
               {
                   Console.WriteLine(dgr.sobjects[i].name);
               }
           }
           catch (Exception e)
           {
               Console.WriteLine("An exception has occurred: " + e.Message + "\nStack trace: " + e.StackTrace);
           }
           #endregion

           #region describeSObjectSample
           try
           {
               string objectType = myObjectType;

               // Call describeSObjects() passing in an array with one object type name
               DescribeSObjectResult[] dsrArray;
               client.describeSObjects(header, null, null, new string[] { objectType }, out dsrArray);

               // Loop all fetched occurrences; possibly just one in this sample
               for (int arrayCount = 0; arrayCount < dsrArray.Length; arrayCount++)
               {
                   DescribeSObjectResult dsr = dsrArray[arrayCount];

                   // Get some object properties
                   Console.WriteLine("\n\nObject Name: " + dsr.name);
                   if (dsr.custom) Console.WriteLine("Custom Object");

                   // Get the permissions on the object
                   if (dsr.label != null) Console.WriteLine("Label: " + dsr.label);
                   if (dsr.createable) Console.WriteLine("Createable");
                   if (dsr.deletable) Console.WriteLine("Deleteable");
                   if (dsr.queryable) Console.WriteLine("Queryable");
                   if (dsr.replicateable) Console.WriteLine("Replicateable");
                   if (dsr.retrieveable) Console.WriteLine("Retrieveable");
                   if (dsr.searchable) Console.WriteLine("Searchable");
                   if (dsr.undeletable) Console.WriteLine("Undeleteable");
                   if (dsr.updateable) Console.WriteLine("Updateable");
                   Console.WriteLine("Number of fields: " + dsr.fields.Length);

                   //Retrieve metadata for each field
                   for (int i = 0; i < dsr.fields.Length; i++)
                   {
                       // Get the field and Write some field properties
                       Field field = dsr.fields[i];
                       Console.WriteLine("Field name: " + field.name);
                       Console.WriteLine("\tField Label: " + field.label);

                       //This next property indicates that this field is searched when using the name search group in SOSL
                       if (field.nameField) Console.WriteLine("\tThis is a name field.");
                       if (field.restrictedPicklist) Console.WriteLine("This is a RESTRICTED picklist field.");
                       Console.WriteLine("\tType is: " + field.type.ToString());
                       if (field.length > 0) Console.WriteLine("\tLength: " + field.length);
                       if (field.scale > 0) Console.WriteLine("\tScale: " + field.scale);
                       if (field.precision > 0) Console.WriteLine("\tPrecision: " + field.precision);
                       if (field.digits > 0) Console.WriteLine("\tDigits: " + field.digits);
                       if (field.custom) Console.WriteLine("\tThis is a custom field.");

                       // Write the permissions of this field
                       if (field.nillable) Console.WriteLine("\tCan be nulled.");
                       if (field.createable) Console.WriteLine("\tCreateable");
                       if (field.filterable) Console.WriteLine("\tFilterable");
                       if (field.updateable) Console.WriteLine("\tUpdateable");

                       // If this is a picklist field, show the picklist values
                       if (field.type.Equals(fieldType.picklist))
                       {
                           Console.WriteLine("\tPicklist Values");
                           for (int j = 0; j < field.picklistValues.Length; j++)
                               Console.WriteLine("\t\t" + field.picklistValues[j].value);
                       }

                       // If this is a foreign key field (reference), show the values
                       if (field.type.Equals(fieldType.reference))
                       {
                           Console.WriteLine("\tCan reference these objects:");
                           for (int j = 0; j < field.referenceTo.Length; j++)
                               Console.WriteLine("\t\t" + field.referenceTo[j]);
                       }
                       Console.WriteLine("");
                   }
               }
           }
           catch (Exception e)
           {
               Console.WriteLine("An exception has occurred: " + e.Message + "\nStack trace: " + e.StackTrace);
           }
           #endregion
       }
       #endregion

       #region queryLeads
       private String queryLeads()
       {
           string myXML = @"<Leads>";

           try
           {
               String soqlQuery = "SELECT Id, FirstName, LastName, Company, State, Email, Status, CreatedDate FROM Lead ORDER BY LastName";
               //String soqlQuery = "SELECT FirstName, LastName, OwnerId FROM Lead ORDER BY LastName";
               QueryResult qr;
               client.query(header, null, null, null, soqlQuery, out qr);
               bool done = false;

               if (qr.size > 0)
               {
                   Console.WriteLine("Logged-in user can see " + qr.records.Length + " records.");

                   while (!done)
                   {
                       Console.WriteLine("");
                       sObject[] records = qr.records;
                       for (int i = 0; i < records.Length; i++)
                       {
                           //Contact con = (Contact)records[i];
                           Lead lead = (Lead)records[i];
                           string fName = lead.FirstName;
                           string lName = lead.LastName;

                           if (fName == null) Console.WriteLine("Record #" + (i + 1) + ": " + lName);
                           else Console.WriteLine("Record #" + (i + 1) + ": " + lName + ", " + fName);

                           myXML += "<Lead><Name>" + lName + ", " + fName + "</Name><Company>"
                               + lead.Company + "</Company><StateProvince>" + lead.State + "</StateProvince><Email>"
                               + lead.Email + "</Email><LeadStatus>" + lead.Status + "</LeadStatus><CreatedDate>"
                               + String.Format("{0:yyyy-MM-ddTHH:mm:ssZ}", lead.CreatedDate) + "</CreatedDate><LeadID>"
                               + lead.Id + "</LeadID></Lead>";
                       }

                       if (qr.done) done = true;
                       else
                       {
                           client.queryMore(header, null, qr.queryLocator, out qr);
                       }
                   }
               }
               else Console.WriteLine("No records found.");
           }
           catch (Exception e)
           {
               Console.WriteLine("\nFailed to execute query succesfully," + "error message was: \n{0}", e.Message);
           }

           /*XDocument xdoc = new XDocument();
           xdoc = XDocument.Parse(myXML);
           return xdoc;*/
           return myXML + "</Leads>";

       }
       #endregion

       #region createLead
       private String createLead(String FirstName, String LastName, String Company, String Email)
       {
           String confirmationId = "0";
           try
           {
               Lead myNewLead = new Lead();
               /*myNewLead.Description = "incoming from Bizagi";
               myNewLead.Salutation = "Mr";*/
               myNewLead.FirstName = FirstName;
               myNewLead.LastName = LastName;
               myNewLead.Company = Company;
               myNewLead.Email = Email;

               sObject[] objects = { myNewLead };

               SaveResult[] results;
               LimitInfo[] limitinfo;

               client.create(
                   header,
                   null,
                   null,
                   null,
                   null,
                   null,
                   null,
                   null,
                   null,
                   null,
                   null,
                   null,
                   objects,
                   out limitinfo,
                   out results
               );
               
               SaveResult tmp = results[0];
               if (tmp.success)
               {
                   Console.WriteLine("Created lead Id is: " + tmp.id + ".");
                   confirmationId = tmp.id;
               }

           }
           catch (Exception e)
           {
               Console.WriteLine("\nFailed to execute query succesfully," + "error message was: \n{0}", e.Message);
           }
           return confirmationId;
       }
       #endregion

       #region logout
       private void logout()
       {
           try
           {
               client.logout(header);
               Console.WriteLine("Logged out.");
           }
           catch (Exception e)
           {
               Console.WriteLine("An unexpected error has occurred: " + e.Message + "\nStack trace: " + e.StackTrace);
           }
           Console.WriteLine("\nPress ENTER to continue...");
           Console.ReadLine();
       }
       #endregion
       #endregion

   }
}

 

 

 

Notice that the class called Class1 offers 2 methods which serve as entry points for the logic that Bizagi will need to use from the processes perspective.

These 2 methods are:

public String Query(String userName, String password, String securityToken): To fetch all leads by providing authentication parameters.

public String Create(String userName, String password, String securityToken, String firstName, String lastName, String company, String email): To create a new lead, by providing authentication parameters along with the 4 basic and mandatory fields of a new lead: a first name, last name, the company he/she works for, and an e-mail address.

 

Both the class name (Class1) and each of these above methods will be referenced by their exact name in further steps.

 

note_pin

Consider that the above code is provided as is, mainly as a sample code that illustrate a possibility when integrating to Salesforce.

You may perform any modifications to extend the functionality in it (e.g to consider managing Contacts, Opportunities or Accounts, to consider API changes or resolutions for issues directly introduced by Salesforce, or to use an app.config configuration file that manages Salesforce credentials to avoid hard-coding information at most).

 

For the public String Query method, the code above returns a string with an XML format having the following structure:

<ClaimandComplaintRequest>

<Leads>

<Lead><Name>Akin, Kristen</Name><Company>Aethna Home Products</Company><StateProvince>VA</StateProvince><Email>kakin@athenahome.com</Email><LeadStatus>Working - Contacted</LeadStatus><CreatedDate>2015-12-28T17:07:00Z</CreatedDate><LeadID>00Q1500000sGMpKEAW</LeadID></Lead>

</Leads>

</ClaimandComplaintRequest>

The reason it returns an XML-formatted string, is to enable an easier handling of information directly in Bizagi, by relying on the rules API and the use of scopes or Xpath technologies in Bizagi.

 

1.11 Build the output

Build your project so that it produces the class library output you need in Bizagi.

Do this while ensuring it is generated for your target system architecture (32-bit or 64-bit system) and .NET framework version:

 

Sforce_visual2

 

The image above shows the MySalesforceLibrary.dll class library output (as named by the sample steps presented before).

Acknowledge on where is this output generated, as it will be needed in further steps and configuration.

 

1.12 Test your component from another Visual Studio project

Though this an optional step, it is strongly recommended to ensure that your component works when invoked by another project.

This other project can be one directly created in your same Visual Studio solution, namely a Console project.

 

note_pin

Note that Bizagi would invoke the class library just as a Console project does, from a logical standpoint.

While ensuring you have no coding errors before moving into Bizagi, you achieve most efficient roundtrips regarding code adjustments.

 

To do this, create a new Console project to this same solution:

 

Sforce_visual3

 

This will help you test and verify your component (for instance, viewing directly in console output those fetched leads):

 

Sforce_outputleads

 

Ensure you include your class library as a reference:

 

Sforce_testconsole

 

Then, copy and paste exactly the same contents from the app.config file of the Salesforce class library project, and into the new app.config created for your new Console project.

This means that the app.config or the Console project should define the same endpoint address, and the settings used for the maxBufferSize, maxReceivedMessageSize and maxBufferPoolSize, as shown below:

 

,Sforce_appconfig

 

 

For the main class (called TestConsole in this example), you may use the following code, and fill in the blanks for userName, password and securityToken:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using MySalesforceLibrary;

 

namespace TestConsole

{

   class Program

   {

       

       static void Main(string[] args)

       {

           String userName = "";                //your account's username here

           String password = "";                //your password here

           String securityToken = "";        //your security token here

           String objectType = "Lead";

 

           Console.WriteLine("Press an option (1-4) to run a test:");

           Console.WriteLine("\t" + "-> 1 to test the connection only");

           Console.WriteLine("\t" + "-> 2 to test querying all leads");

           Console.WriteLine("\t" + "-> 3 to test creating one lead");

           Console.WriteLine("\t" + "-> 4 to test all of the above");

 

           Class1 pr = new Class1();

           String option = Console.ReadLine();

           int optionInt;

           if (Int32.TryParse(option, out optionInt))

           {

               switch (optionInt)

               {

                   case 1:

                       pr.Connect(userName, password, securityToken, objectType);

                       break;

                   case 2:

                       Console.WriteLine(pr.Query(userName, password, securityToken));

                       break;

                   case 3:

                       Console.WriteLine(pr.Create(userName, password, securityToken,

                           "Jerome", "Jones", "AgilityCorp", "jj@agilitycorp.com"));

                       break;

                   case 4:

                       pr.Test(userName, password, securityToken, objectType,

                           "Jerome", "Jones", "AgilityCorp", "jj@agilitycorp.com");

                       break;

                   default:

                       Console.WriteLine("Not a valid option. Ending execution.");

                       break;

               }

           }

       }

   }

}

 

Running the Console project as per the sample code above, will allow you to test separately both the connectivity or all methods used in this example (or you may extend these possibilities if you implement additional methods):

 

Sforce_outputtest

 

 

 

 

2. Register the component in Bizagi Studio.

Once you have the built class library output and verified its adequate working, open up your project with Bizagi Studio.

 

2.1 Open the Component library

Browse to the Tools tab in the ribbon and click on the Components option.

Click on Add to register your Salesforce component:

 

Sforce_Bizagi1

 

 

2.2 Enter the component's details

Enter the details for Display name and Description. Finally, locate and upload the class library assembly, which in this example is MySalesforceLibrary.dll:

 

Sforce_Bizagi2

 

Click Ok.

 

2.3 Enter the component's details

Similarly as done with the Test Console project, you need to use the same definitions that allow Bizagi as an application to run and transmit Salesforce services.

To do so, copy the contents from the app.config file of the Salesforce class library project, specifically those inside of the <system.serviceModel> element which are <bindings> and <client>.

 

Sforce_Webconfig2

 

From a windows explorer, locate the web.config file of Bizagi and edit it (by default at C:\Bizagi\Projects\[your_project]\WebApplication).

To edit it, first locate its <system.serviceModel> element and then paste such copied content inside of this element:

 

Sforce_Webconfig

 

Save the file when done.

From this point on, the component has been bundled and can be used freely in any business rule of your processes.

 

note_pin

As per the configuration carried out above, the way to use the 2 methods in a business rule is:

MySalesforceLibrary.Class1.Query(...)

MySalesforceLibrary.Class1.Create(...)

 

3. Configure a business rule that invokes your component.

Before configuring any given business rule to use the already registered component, you will need to previously ensure you have the appropriate data model definitions and UI options that handle Salesforce data (e.g, the lead's name, e-mail or company fields in Bizagi).

To illustrate this part and produce a fully-running application, recall that we will go through the changes needed in the Petition, Claims and Complaints template process as directly downloaded from the Process Xchange.

 

3.1 Create an entity called Lead

Edit the data model so that it now has an entity called Lead.

To do this, go to the Data model diagram of the process (process wizard step #2) and create a new master entity:

 

Sforce_model2

 

 

3.2 Create attributes for the entity

The Lead entity's data structure should match that information used by a Salesforce Lead that is considered by the component.

Therefore, create the following attributes which correspond to those used by the provided code example: Name, Company, StateProvince, Email, LeadStatus, and CreatedDate.

 

Sforce_model1

 

3.3 Create an entity relationship

Create a new relationship in order to connect the current data model (from the starting point process entity called Claim and Complaint request), to the newly created Lead entity, .

To do so, click on the Relationship option:

 

Sforce_model3

 

For this relationship, ensure you select Claim and Complaint request as the Entity1, and Lead as the Entity2:

 

Sforce_modelR1

 

Then, select the Collection (one-to-many) option.

This will allow the user interfaces to present a table having a list of leads, those which will be fetched by Bizagi from Salesforce.

Bizagi will name this relationship automatically as Leads.

 

Sforce_modelR3

 

 

Now that changes in the data model have been completed, the data model diagram should display the new relationship that connects the Claim and Complaint request entity to the Lead entity:

 

 

Sforce_model5

 

3.4 Open the forms

We now proceed to edit the existing user interfaces.

To do so, go to the process wizard step #3 called Define forms, and open the form of the Analyze suggestion activity:

 

 

Sforce_Bizagi3

 

Notice that you may use that information of the requester which is already stored at this point (this set of information is the one to be submitted as a new lead in Salesforce).

Therefore, adjusting the user interfaces to present the possibility of creating a lead in Salesforce only needs a button with its respective click action.

 

3.5 Add a new button

Locate the Button control to add it to the requester's information group:

 

Sforce_Bizagi6

 

Ensure you configure its Display name and Button caption, and then click on Actions and validations to define its click action:

 

Sforce_Bizagi7

 

3.6 Define the button's action

Add a new action, which will trigger integration with Salesforce when the Create as lead button is clicked.

Configure the action to execute a rule, as shown below:

 

Sforce_Bizagi9

 

For the argument configuration, create a new business rule:

 

Sforce_Bizagi8

 

The code in the rule should have:

var myComponent = new MySalesforceLibrary.Class1();

myComponent.Create("", "", "",

 <ClaimandComplaintRequest.Customer.FirstName>,

 <ClaimandComplaintRequest.Customer.LastName>,

 "Agility Corp.",

 <ClaimandComplaintRequest.Customer.CustomerSnapshot.Email>);

 

note_pin

Notice that this code instances the component by invoking its methods with this syntax: MySalesforceLibrary.Class1.Create(...).

Recall that in this code you will need to enter the userName, password and security token of your own Salesforce account.

 

Save the rule while ensuring that no errors are prompted by Bizagi.

The new action which should look similar to this one below:

 

Sforce_Bizagi10

 

 

Though at this point you may already verify the connectivity with Salesforce from Bizagi and create leads by using the button, we will illustrate as well how to configure that part where we fetch all leads.

 

3.7 Include a table for all leads

While in the same activity's form, create an additional tab:

 

Sforce_Bizagi4

 

Notice you may label it as "Salesforce leads" or with any other name of your choice, though the most relevant part is to make sure you include the Leads relationship of your data model (i.e a collection, to be displayed by Bizagi as a table in runtime).

 

3.8 Configure the table

Set this table's properties as read-only (i.e having delete options, add options and row options disabled), then include the relevant information for a Lead (Name, Company, StateProvince, Email, LeadStatus and CreatedDate), as shown below:

 

Sforce_Bizagi5

 

Close and save the form when done.

 

3.9 Configure the business rule from activity actions.

Go to the process wizard step #4 called Activity Actions.

 

Sforce_Step4

 

Add a new activity action for the On Exit timing of the Send Answer activity:

 

Sforce_AA1

 

Create a new business rule having this code:

var myComponent = new MySalesforceLibrary.Class1();

var result = myComponent.Query("", "", "");

CEntityXmlHelper.fromXmlToEntityWithScopes(Me, result,false);

 

Sforce_Rule2

 

Save the rule while ensuring that no errors are prompted by Bizagi, and that's it.

 

note_pin

You may of course enhance this example shown above, which is only for illustrative purposes.

 

For instance and for a live implementation you may want to consider these highly more advanced aspects:

For the component, it is expected and suggested to use a .config file to allow hardened security and better management of the service account's credentials and the security token, instead of hard-coding them in the business rule.

In Bizagi, you are encouraged to use asynchronous integration with Salesforce, to rely on an additional entity in between that interconnects the process entity with Lead (and not so directly), and to use XLST transformations.

Depending on your requirements, you may also want to extend this example and modify both the component and in Bizagi. For instance, so that you can add more attributes to: consider the returned ID of the lead created at Salesforce, allow dynamical input for the name of the company, or to submit additional information to Salesforce (such as telephone numbers, state location, etc).

 

Execution

Execute your process by using the Run button in Bizagi Studio.

Once Bizagi Work portal is opened in browser, create a new case of the Petitions, claims and complaints process:

 

Sforce_Execute0

 

In the first task called Receive request, you will be filling up the details of the requester (document, name,  e-mail), and ensure you select that this request is a Suggestion.

 

Sforce_Execute2

 

Click Next when done.

You will see Bizagi on hold for a moment while fetching records from Salesforce.

 

At the Analyze Suggestion task, you can view all leads at the Salesforce leads tab, or use the Create as lead button to create the new lead:

 

Sforce_Execute1

 

Viewing all the leads:

 

Sforce_Execute3

 

Creating the new lead, while going to Salesforce portal to verify:

 

Sforce_Execute4

 

 

Troubleshooting tips

As stated before, it is strongly recommended that you test the component in Bizagi once you have completely validated that your component's methods work Ok when invoked in another Visual Studio project.

Once you have already verified this, and in order to troubleshoot while being used by Bizagi, debug the component by using the standard "Attach to process..." option, as provided by Visual Studio.

To do so, note that you should attach this to a running IIS process of w3wp.exe:

 

Sforce_troubleshooting

 

For this, you will need to ensure that the component's output is bundled in Bizagi in a Debug mode (to be used only in development environments, for troubleshooting purposes).

 

When doing changes to the component and re-building its output, make sure you upload again this new assembly in Bizagi Studio.

To always force that all changes are taken, you may reset your IIS services and delete temporary files or older versions of the dll files of your components.

 

note_pin

If you get the following error stating that Bizagi "Could not find default endpoint element that references contract 'sforce2.Soap' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.", recall that you need to carry out changes in Bizagi's Work portal configuration file (web.config) as described when registering the component.