Salesforce.com SOAP API Gotchas Part 4

This is the fourth part in a series talking about tricky parts of the Salesforce.com SOAP Partner API (see Part 1, Part 2 and Part 3).

Detecting expired sessions

A robust integration with Salesforce.com must handle invalidated sessions. One source of unexpected invalid session errors is the logout semantics discussed in Part 1. Unfortunately, that’s not the only unpredictable cause of invalid session Ids. The administrator of a Salesforce.com organization can set the session timeout for their users. The session timeout setting is not exposed through the API, though. This makes it impossible to predict whether the session Id for a connection that you haven’t used for a while is still valid. (It’s doubly impossible, in fact: not only can another tool logging in with the same user kill your session by calling logout(), they can also keep your session Id valid by using the API when your application is idle.) Depending on your reliability requirements, it might be worth the effort to automatically detect invalid session Id errors, log in again to get a new session, and retry.

Partner API sample code

There isn’t much documentation provided by Salesforce on how to get started using the Partner API, but this is one thing I can fix myself: I wrote a tutorial in four parts (Part 1, Part 2, Part 3, Part 4).

10,000 character limit on SOQL and SOSL queries

A SOQL query cannot be longer than 10,000 characters. (This limit also applies to SOSL search strings.) It’s surprisingly easy to hit this limit if you’re getting all data for an object in an organization that uses custom fields extensively. You’ll know you’ve hit this limit when you get an exception code of MALFORMED_QUERY (or MALFORMED_SEARCH in the case of SOSL). Given the names, you would think that you can only see this when using query() or search(), but retrieve() can also get MALFORMED_QUERY by retrieving a field list longer than 10,000 characters.

15 vs 18 character Ids

Every type of object (Contact, Account, etc) has a 3-character Id prefix (this post has a table of some common prefixes). An individual Contact might have an Id like this: 0034000000QnQVe. This 15-character Id uniquely identifies a single Contact across all of Salesforce’s data. The Ids appear to be encoded in a form of base 62 (in which the valid ‘numerals’ are 0-9, a-z, A-Z). However, some databases default to case-insensitive mode. This makes searching on a case-sensitive key somewhat difficult, to say the least. So, Salesforce provides a case-insensitive 18-character Id, presumably in base 36 (0-9 A-Z).

Case sensitive vs insensitive Ids

Why 18 characters? 15 case-sensitive (base 62) characters can represent 6215, or around 7.7 * 1026, possible Ids. A 17 character case-insensitive (base 36) Id would represent 3617 = 2.9 * 1026 different Ids, which is less than the number of possible 15-character Ids, but 18 characters is sufficient at 3618 = 1.0 * 1028.

The 18-character Id is formed by appending 3 case-insensitive characters to the 15-character Id, so the case-insensitive 18-character Id will still have both upper and lower case alphabetic characters in it. This might seem confusing, but it’s actually very convenient because it means that you can extract the 15-character Id by simply truncating the last 3 characters of the 18-character Id.

Make sure you consistently use only one form of Id. The only reason to use the 18-character Id is if you have to do case-insensitive string matches. Obviously, if your database supports case-sensitive string matching, the 15-character Id will be faster to match against and use less storage space.

Undocumented WSDL elements

There are a handful of elements that you may occasionally encounter in the WSDL that aren’t documented at all, not even as “Reserved for future use”. Some examples include orgDisallowHtmlAttachments and orgHasPersonAccounts in GetUserInfoResult and calculatedFormula in Field. Naturally, you should not use these unless you like living dangerously.

Validation rule failures may not result in a complete error description

Salesforce.com lets you define validation rules against fields that must be satisfied for a new field value to be allowed. When you provide a value for a field that violates a validation rule, the resulting Error object is supposed to contain a list of one or more fields that caused the error. The actual behavior does not always follow the documentation, though. In this example, I’m trying to set the first name of an Opportunity to a value that starts with ‘Invalid-‘. This should fail because I’ve set a validation rule on First Name that prevents values starting with ‘Invalid-‘.
The update call:

<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <ns2:SessionHeader xmlns="urn:fault.partner.soap.sforce.com"
      xmlns:ns2="urn:partner.soap.sforce.com"
      xmlns:ns3="urn:sobject.partner.soap.sforce.com">
      <ns2:sessionId>(long session id)</ns2:sessionId>
    </ns2:SessionHeader>
    <ns2:CallOptions xmlns="urn:fault.partner.soap.sforce.com"
      xmlns:ns2="urn:partner.soap.sforce.com"
      xmlns:ns3="urn:sobject.partner.soap.sforce.com">
      <ns2:client>client key</ns2:client>
      <ns2:defaultNamespace
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:nil="true"/>
    </ns2:CallOptions>
  </s:Header>
  <s:Body>
    <ns2:update xmlns="urn:fault.partner.soap.sforce.com"
      xmlns:ns2="urn:partner.soap.sforce.com"
      xmlns:ns3="urn:sobject.partner.soap.sforce.com">
      <ns2:sObjects>
        <ns3:type>Opportunity</ns3:type>
        <ns3:Id>0064000000Ak5PMAAZ</ns3:Id>
        <name
          xmlns:ns4="urn:fault.partner.soap.sforce.com" xmlns="">Invalid-Sale one&lt;&amp;'"</name>
      </ns2:sObjects>
    </ns2:update>
  </s:Body>
</s:Envelope>

Response:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns="urn:partner.soap.sforce.com"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <updateResponse>
      <result>
        <errors>
          <message>Name can't start with Invalid-</message>
          <statusCode>FIELD_CUSTOM_VALIDATION_EXCEPTION</statusCode>
        </errors>
        <id xsi:nil="true"/>
        <success>false</success>
      </result>
    </updateResponse>
  </soapenv:Body>
</soapenv:Envelope>

As you can see, there are no <field> elements in the <errors> element, even though there clearly is a field (Name) that caused the error. So, don’t expect that an Error element will necessarily have any fields included.

Partner vs Enterprise Initial Endpoint URLs

The “What’s New” for API version 17 contained this explanation of the new initial endpoint to be used:

https://login.salesforce.com/services/Soap/c/api_version is the new recommended endpoint for API login requests, where api_version specifies the API version, such as 17.0. If you send non-login requests to https://login.salesforce.com/services/Soap/c/api_version, an error is returned. The less secure version of the URL—http://login.salesforce.com/services/Soap/c/api_version—is also supported, but not recommended. It is helpful for debugging through proxy servers.
If you send a login request to the previously recommended endpoint, https://www.salesforce.com/services/Soap/c/api_version, it will still complete successfully, but https://login.salesforce.com/services/Soap/c/api_version is the preferred option.

This is not the complete picture, though.
The Enterprise 17 WSDL has this endpoint:
https://www.salesforce.com/services/Soap/c/17.0
The Partner 17 WSDL has this endpoint:
https://www.salesforce.com/services/Soap/u/17.0

The Enterprise WSDL has a ‘c’ before the version while the Partner WSDL has a ‘u’. So, if you simply follow the instructions from the “What’s New” page, you’ll try to use
https://login.salesforce.com/services/Soap/c/17.0 as the endpoint for your Partner application, which will throw the following relatively unhelpful exception:
javax.xml.ws.soap.SOAPFaultException:
No operation available for request {urn:partner.soap.sforce.com}login

The correct new-style endpoint for a Partner version 17 connection is https://login.salesforce.com/services/Soap/u/17.0

  • Digg
  • StumbleUpon
  • del.icio.us
  • Facebook
  • Twitter
  • Google Bookmarks
  • DZone
  • HackerNews
  • LinkedIn
  • Reddit
  • Anil

    Hi,
    I’m writing an SOAP integration code for salesforce.com enterprise (WSDL)customer,What I see the binding URL in enterprise WSDL contains URL which looked like the URL which I get after successful login request.

    Is there any difference between enterprise WSDL that gets generated from developer account & customer account.

    And If It is customer’s enterprise WSDL,do I still need to post SOAP login request to get actual URL to post create() request .

    Please advice..

    Thanks
    Anil Sharma

    • Marshall Pierce

      The enterprise WSDL is (potentially) unique per organization. I don’t think it differs based on which user was logged in when the WSDL was generated.

      According to http://www.salesforce.com/us/developer/docs/api/Content/sforce_api_quickstart_steps.htm#step_4_walk_through_the_sample_code you always should use the login result to get the server url for subsequent requests.

      • http://www.riverbed.com Bill

        Actually, the WSDL is generated according to the permissions of the user generating the WSDL.

        This means, for instance, if you don’t have permission to view a particular field, it won’t be listed. Otherwise, anybody could get a complete layout of a company’s implementation as soon as they have *any* access.

        In the end, what is available to a particular application is whatever is available to the account being used in that connection. The WSDL is just to help your app know what is available, Salesforce has absolutely no idea which wsdl you’re using, if any at all.

        • Marshall Pierce

          Good to know. Thanks for the correction.

  • Will

    From SFDC:

    18-character ID values are simply 15-character ID values with a checksum suitable for case-insensitive searches. If you are using a case-sensitive application to match up ID values, you may safely discard the checksum characters. Otherwise, you will need to generate a checksum from the report’s 15-character ID values to obtain the 18-character ID value. This is done by creating an zero-based indexed array of [A..Z0..9], then grouping the 15 characters into three groups of 5 characters. Each group creates a bitmask, where 1 is if the letter is uppercase, 0 if lowercase or a number. This five-bit number indexes into the array above. For example:

    15-character ID: 00130000000Az53

    Bitmask grouping: 00130 | 00000 | 0Az53

    Bitmask: 00000 | 00000 | 01000

    Indexing: array[0] = “A”; array[0] = “A”; array[8] = “I”;

    Final 18-character ID: 00130000000Az53AAI

    Hopefully this makes some sense. If you are using case-sensitive environments, it’s easier to just ignore the final three characters. However, if you need to use the ID values in a case-insensitive manner, this is how you can convert the report values to case-sensitive ID values.

  • Pingback: A new Java Salesforce API Library | Team Lazer Beez Blog

  • Pingback: Salesforce.com SOAP API Gotchas Part 3 | Team Lazer Beez Blog

  • Pingback: Salesforce.com SOAP API Gotchas Part 1 | Team Lazer Beez Blog