Saturday, November 9, 2013

Salesforce Get Deleted/Get Updated

Salesforce has just started releasing the Winter ’14 update which contain a couple new features in the REST API. You can view the full release notes here:

https://help.salesforce.com/help/doc/en/salesforce_winter14_release_notes.pdf

In this post I want to talk about the new Get Deleted and Get Update resources. As the names suggest these resources are used to retrieve lists of recently deleted and update records. These are specifically designed to facilitate replication of data. For example, if you are developing a mobile app you may want to store a local copy of some of the contact fields. These resources will help you keep that local copy in sync.

Get Deleted

First, let’s look at Get Deleted. The URI for this resource looks like this:

/services/data/v29.0/sobjects/Contacts/deleted/
?start=2013-05-05T00:00:00+00:00&end=2013-05-10T00:00:00+00:00

This will return a list of all Contacts that were deleted between the specified start and end dates.
There are a couple things you need to be careful of when specifying the dates. First, the dates are UTC not local time, second they must be in ISO 8601 format and third the start date has to been within 30 days of the current date.

Let’s talk a little more about how to format the dates. If you want to send the date in the “2013-05-05T00:00:00+00:00” format you must UrlEncode it because of the plus sign. This can be done in .NET like this:

System.Web.HttpUtility.UrlEncode(“2013-05-05T00:00:00+00:00”)

Note that you may need to add a reference to the System.Web assembly if you are developing a desktop application. It turns out that there is no simple way to generate this format date in .NET short of using a custom format string. As an alternative you can use a slight variation of this format, "2013-10-08T04:00:00Z". There is a standard format that come pretty close to this but puts a space between the time and date instead of the ‘T’, but this can easily be fixed. Here is an example of how to convert a DateTime variable in local time to the properly formatted UTC time:

dtStart.ToUniversalTime().ToString("u").Replace(" ","T");

When you make the Get Deleted call you will get a JSON result that looks like this:

{
  "deletedRecords" : [ {
    "id" : "003E000000kkhvtIAA",
    "deletedDate" : "2013-10-09T12:17:12.000+0000"
  }, {
    "id" : "003E000000kle9zIAA",
    "deletedDate" : "2013-10-09T19:03:28.000+0000"
  }, {
    "id" : "003E000000kmVqiIAE",
    "deletedDate" : "2013-10-11T11:46:00.000+0000"
  } ],
  "earliestDateAvailable" : "2012-10-28T16:00:00.000+0000",
  "latestDateCovered" : "2013-10-11T12:05:00.000+0000"
}


you will see an array called deletedRecords which contains the id of the deleted record and the date it was delete, again in UTC. In a synchronization scenario you would use these id’s to delete the records from the local copy. The lastDateCovered value is the last date that the query looked at for deleted records. You would save this value and use it as the start date for you next Get Delete query. The API documentation says that earliestDateAvailable is “timestamp (Coordinated Universal Time (UTC)—not local— timezone) of the last physically deleted object.”, although I haven’t been able to figure out what that really means.


Get Updated

Get updated works like Get Deleted, but returns a list of the ID’s of objects that were either added or modified between the specified dates. The URI for this resource looks like this:

/services/data/v29.0/sobjects/Contacts/updated/?start=2013-05-05T00:00:00+00:00&end=2013-05-10T00:00:00+00:00

All the rules for dates are the same as they are for Get Deleted. When you make a Get Updated call you will get back a JSON result that looks like this:


{
  "ids" : [ "003E000000QCZ1kIAH", "003E0000001ZufeIAC", "003E000000kmVoiIAE" ],
  "latestDateCovered" : "2013-10-21T10:59:00.000+0000"
}


The ids array contains the id of each object that was either added of modified. Just like with Get Deleted, lastestDateCovered is the last date that the query looked at for updated records. You would save this value and use it as the start date for you next Get Updated query

Sunday, September 22, 2013

Salesforce REST Exceptions

I get a lot of emails from people working with the Salesforce REST who say that they are getting a 400 error (Bad Request) when trying to perform one of the API functions. In this article I will talk about this error and how to troubleshoot it as well as other API errors.
For the purpose of this article I will assume you are using an HttpWebRequest object to make your requests to Salesforce. The function on HttpWebRequest that does the actual work is  GetResponse. This function makes the call to the remote server and retrieves the response. If anything goes wrong with a REST API request on the Salesforce server it will generate an 400 HTTP error response code and then GetResponse will throw a WebException containing that status code. The 400 response code isn’t too useful since there are a lot of things that can cause it, but if you catch the exception you can still access the response stream which will contain a JSON object with more details.
As an example, if we make a call to get an Access Token and pass an incorrect client_secret you will get this web exception:
"The remote server returned an error: (400) Bad Request."
If we then get the response stream we find this:
{\"error\":\"invalid_client\",\"error_description\":\"invalid client credentials\"}
Here is another example. If we issue a query command with a syntax error in the SOQL query (SELEC id,accountNumber,name from account), you will get this:
{ \"message\" : \"unexpected token: SELEC\",\n  \"errorCode\" : \"MALFORMED_QUERY\"}
You can see that it didn’t recognize “SELEC” since it is missing the ‘T’. Note that the format of the JSON in this message is different then the first one.
So what is the best way to handle this? There are a number of options depending on you needs, but here is one suggestion. Here is the piece of code that makes the web request that would be in a library used by all Salesforce REST calls. ‘req’ is  a HttpWebRequest object.

req.Method = "GET";
System.Net.WebResponse resp = null;
try
{
    resp = req.GetResponse();
}
catch (WebException ex)
{
    string msg = "";

    if (ex.Status == WebExceptionStatus.ProtocolError)
    {
        resp = ex.Response;
        msg = new System.IO.StreamReader(resp.GetResponseStream()).ReadToEnd().Trim();
    }

    throw new SalesforceException(msg, ex);
}


In the try block we make the call to Salesforce and get the response. Next we catch only WebExceptions, other exceptions are allow to percolate up to the next level which is a good practice for library functions. Next we check that status of the WebException. There are a lot of things that can cause WebExceptions but most of them will result in a response not being returned, so we just look for a ProtocolError. If we do have a protocol error we retrieve the JSON error message from the response. Finally we create and throw a custom exception I call SalesforceException that will hold both the JSON message and the original exception in the InnerException. The code for the SalesforceExpcetion is here:


public class SalesforceException : Exception 
{
    public SalesforceException(string message, Exception innerException)
        : base(message, innerException) { }
}


One other thing to note about Salesforce REST errors. Since REST commands are based on URIs, if you have an error in the URI you will normally get back a HTTP 404 error instead of a 400 error. For example if we send a read command with an invalid object ID in the URI, we will get a 404 error instead of a 400 error.