Tuesday, September 20, 2016

Salesforce upserts, SOAP & standard object lookups

Requirement

Need to perform a Salesforce upsert via SOAP API using External IDs on standard objects.

e.g. you want to specify the record Owner or RecordType without using Salesforce IDs.

Getting set up


Don't bother trying to prototype using Data Loader. All the standard object external IDs are unavailable. AFAICT Workbench doesn't support them either.
  1. Generate the WSDL and strip out any comments and script
  2. Install SoapUI if you haven't already
  3. Create a new SOAP project using your WSDL
  4. Find the login method and set it up as below. Get rid of any question marks
  5. Press play. The response appears on the right hand side.
This example is for the Enterprise WSDL.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:enterprise.soap.sforce.com">
   <soapenv:Header>
      <urn:LoginScopeHeader>
         <urn:organizationId></urn:organizationId>
         <!--Optional:-->
         <urn:portalId></urn:portalId>
      </urn:LoginScopeHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:login>
         <urn:username>me@myorg.org.sandbox</urn:username>
         <urn:password>reallybadpassword</urn:password>
      </urn:login>
   </soapenv:Body>
</soapenv:Envelope>

Upsert time

Find the Upsert method, set it up as below and press play. Copy the session ID from the login response to it.

  • The server URL returned in the login response is the target for this request. Put it up in the url box at the top of the request window
  • Account doesn't have any standard external ID fields so a field called ExternalID__c has been created. You could use the ID field instead, but yeuch
  • The Account has a record type with Name "Record Type 1"
<soapenv:Envelope
    xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:urn="urn:enterprise.soap.sforce.com"
    xmlns:urn1="urn:sobject.enterprise.soap.sforce.com"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <soapenv:Header>
      <urn:SessionHeader>
        <sessionId>00Dm00000001xVa!ARQAQCyXrqpRsPctsGUj4AAHIKJJlBeOjxM9M.GOGMCjsYrNNAHJ6OTRIYxZLwbjXb.5bEZh66q09rhYecRDl4YNUc0_7ruO</sessionId>
      </urn:SessionHeader>
   </soapenv:Header>
   <soapenv:Body>
      <urn:upsert>
         <urn:externalIDFieldName>ExternalId__c</urn:externalIDFieldName>
         <urn:sObjects xsi:type="urn1:Account">
            <urn1:ExternalId__c>abcd1234</urn1:ExternalId__c>
            <urn1:Owner xsi:type="urn1:User">
               <urn1:Email>me@myorg.org</urn1:Email>
            </urn1:Owner>
            <urn1:RecordType xsi:type="urn1:RecordType">
               <urn1:Email>Record Type 1</urn1:Email>
            </urn1:RecordType>
         </urn:sObjects>
      </urn:upsert>
   </soapenv:Body>
</soapenv:Envelope>

Other things to note

To find out which standard object fields are available as external IDs, use the Workbench and look for idlookup:true" on a field.

If you're a freetard using the dead-ended version of SoapUI (5.2.1) then you'll need to enable TLS 1.2 as per instructions at the following link...(or so I've heard)

https://community.smartbear.com/t5/SoapUI-Open-Source/How-to-enable-TLS-1-2-in-SoapUI/td-p/96239/page/2

The correct instruction in this topic for Windows:

Place this in your SoapUI-X.Y.Z.vmoptionsf file (where X.Y.Z is the version number of SOAPUI you have)

-Dsoapui.https.protocols=TLSv1.2,SSLv3


The User object doesn't have any fields where both idlookup and unique are true. As a result, to do an upsert involving Users, the session user requires the incredibly broad View All Data rights, otherwise they will get this error: "INSUFFICIENT_ACCESS: Upsert requires view all data on a non-unique custom index". This despite UserNames being unique across the entire platform!

The RecordType Name field can be used as an external ID, but the DeveloperName field can't, which is odd given that the DeveloperName is recommended as the invariant reference for apex code.

Monday, January 11, 2016

Salesforce HYPERLINK expression and communities

Problem


A text formula field providing a url as follows doesn't work for a named community, so only works internally.

HYPERLINK( "/"+Parent_Custom_Object__r.Id, Parent_Custom_Object__r.Name,"_self")

e.g. if you have a community called mysite, you want the following url for users logging into this site

https://mysite-mydomain.instance.force.com/mysite/0ZZ123DEF456GHI

but instead you get

https://mysite-mydomain.instance.force.com/0ZZ123DEF456GHI

which only works for internal users and gives an Under Construction error for communities users.

Solution

HYPERLINK( LEFT($Api.Partner_Server_URL_260, FIND( '/services', $Api.Partner_Server_URL_260))+Parent_Custom_Object__r.Id, Parent_Custom_Object__r.Name,"_self")

Since the Partner_Server_URL_nnn is served up by the $API variable, I thought it wouldn't be sensitive to user context. But it is, which means this works for communities! It could be either $API or the UI itself which is converting to a communities-friendly URL, I don't know which.