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.

Saturday, December 6, 2014

Windows 8 Pro and multiple simultaneous logins

If you have Windows 8 Pro then Remote Desktop is available for you to log in remotely. Great! However with the version of RDP that comes with Win 8 Pro, only one login is allowed at a time. If someone is using the PC and someone else logs in remotely, the console user (and any other remotely logged in users) will get kicked off.

Enabling multiple simultaneous logins involves updating a system file, which also means changing file security so you can work with the file. I had to refer to a few different sources to figure out how to achieve this.

So for next time these are my consolidated steps for this:

  1. Bring up the Task Manager and stop Terminal Services (may also need to stop UMRDPService)
  2. In the Start menu, type File Explorer, and when it comes up, right-click and Run As Administrator. Say OK to any dialog boxes that pop up
  3. Find termsrv.dll in the c:\windows\system32 folder, right-click and select properties
  4. Select the Security tab, click the Advanced button
  5. Next to Owner, click Change
  6. Type Administrators in the text box and click Check Names. Your local administrators group should be found (named YourPC\Administrators). Click OK
  7. Click the Auditing tab, click Add then click Select a Principal
  8. Type Administrators in the text box and click Check Names. Your local administrators group should be found (named YourPC\Administrators). Click OK
  9. For Basic permissions, tick Full Control and click OK in all dialog boxes until you're back to File Explorer
  10. Right-click termsrv.dll again, select properties, click the Security tab and click Edit
  11. In the Groups or User Names box, make sure you have Administrators selected then click Full Control and click OK until you're back to File Explorer
  12. Copy termsrv.dll to a backup location. Can't be too careful!
  13. Download zip file from http://cdn.nextofwindows.com/download/win81rdp.zip
  14. Now you need to figure out which file to use. If your Windows is 32 bit then you want to use 32_termsrv.dll, otherwise 64_termsrv.dll. If you're not sure then compare the size of your existing file with these two - you want to use the one that's the same size (or nearly). Extract the dll you want from the zip and remove the prefix so the name is termsrv.dll.
  15. Copy the new file into c:\system\windows32
  16. Restart Terminal Services (and also UMRDPService if you have it)


References:

Kent Chen's info about the replacement dll
http://www.nextofwindows.com/how-to-allow-multiple-concurrent-users-log-in-windows-8-through-remote-desktop/

Usman Javaid's info about changing system file permissions
http://www.addictivetips.com/windows-tips/take-ownership-of-files-folder-and-change-permissions-in-windows-8/

Tuesday, August 5, 2014

profileID attribute is either null or invalid

Error

Salesforce Community Self-registration attempt fails with the following error:

A new user's attempt to register at failed because the value for the profileID attribute is either null or invalid. Set the profileID for new users on the ChatterAnswersRegistration Visualforce page for the site associated with community  or on the apex class associated with the Facebook authprovider.

Possible Cause


CommunitiesSelfRegController.cls doesn't have a valid ProfileId

The error doesn't mention this class at all.

Saturday, July 19, 2014

Invalid Id: An unexpected error has occurred. Your development organization has been notified

This obscure Salesforce error appears from time to time when fetching a custom url parameter in an extension controller class.

Id recordId;
recordId = ApexPages.currentPage().getParameters().get('rid');
if(rid!=null&&rid!=''){ //ERROR REPORTED ON THIS LINE

Resolution is to change the data type to String. There seem to be more implicit conversions from String to Id than the other way.

Friday, June 6, 2014

apex soql subselect weirdness with "with sharing"

I had some code similar to the following:

Public with sharing class MyClass{
...
    ParentObject__c[] pos = [select Id, (select MasterDetail_2__c from ChildObjects__r)
        from ParentObject__c];
...
   Set MasterDetail_1s = new Set();
    for(ParentObject__c po:pos){
        for(ChildObject__co: po.ChildObjects__r){
            MasterDetail_1s.add(co.MasterDetail_1__c);
        }
    }
...
}

But I was not getting any child records back in the select. Infuriatingly, running the query in the Developer Console Query Editor/Force.com IDE Schema/Force.com Explorer returned the expected results.

While checking that the problem wasn't related to permissions, I changed "with sharing" to "without sharing" and then immediately the following error was reported:

SObject row was retrieved via SOQL without querying the requested field: ChildObject__c.MasterDetail__1__c

With this simplified version of the code it's obvious there is a problem: ChildObject__c.MasterDetail_1__c is used without having been queried. However this error wasn't being reported when the class was declared as "with sharing".

In summary: if subselects are not behaving, "with sharing" may be hiding the errors. Switch to "without sharing" for debug purposes, fix the code and switch back again.

Edit: it actually seems to be more complicated than this - the code stopped working again even with "without sharing". The actual code had something more like

MasterDetail_1s.add(String.valueOf(co.MasterDetail_1__c));

which appeared to cause a problem, but again no exception was raised. I avoided this by removing the explicit conversion.

Tuesday, March 25, 2014

Installing Force.com IDE on Kepler behind proxy and firewall

I'm working behind proxy but also a mean, mean firewall. I can't download anything unless the site is specifically whitelisted. I couldn't even download Kepler without getting approval from a security team.

So naturally my Force.com IDE install will have some problems because it needs to go off and fetch resources from around the web.

Firstly the old site (http://www.adnsandbox.com/tools/ide/install/) seems to work better than the new one. It looks like plenty of people are aware of this.

My solution for getting the rest of the install working was to track down random versions of the files Eclipse was complaining about and drop them in the plugins directory. Eclipse has an install option "Update my installation to be compatible with the items being installed" which will update these files if they're out of date. But before you can even select this option, you have to have some version of the file in the right location.

The files I needed were:

  • org.apache.log4j - copied it from my old eclipse helios install
  • org.eclipse.update.ui - not in helios install, found on java2s.com
The first time I tried to install, I got another error. I tried again, and I got past it and was asked to restart Eclipse to complete installation. So it looks like the first attempt was partially successful which allowed the second attempt to complete.