• Salesforce Streaming API and IE 11

    The Salesforce Streaming API is a wonderful tool that can help you create UIs that stay up to date with your data in real time. You also might find that it mysteriously doesn’t work in IE11, which Salesforce still officially supports on the platform.

    Problem 1: Salesforce documentation is horribly out of date

    The official documentation has you installing CometD version 2.2 (from 2011) against Salesforce API version 24. This version is way to old to work with IE. I found a blog post from 2014 that determined you need at least 2.9

    In addition, as of Summer 17, Salesforce only supports CometD 3.0.9 or later so I recommend using that or the latest version 3.1.1 from download.cometd.org as I did.

    Problem 2: CometD folder structure has changed

    • jQuery is no longer included in the CometD download. Grab the latest jQuery from jquery.com. You need at least 3.1.0 but I used 3.2.1
    • json2.js is no longer included in the CometD download. Theoretically this is no longer needed as it is only for IE8 and below and SF only supports IE11 so ignore it.
    • For CometD you only need two files but they need to be extracted first. Extract the CometD download then go to cometd-javascript/jquery/target right click the .war file and “open with” an archive utility. You need js/cometd/cometd.js and js/jquery/jquery.cometd.js from the resulting files

    Problem 3: CometD defaults to WebSockets

    Salesforce doesn’t support WebSockets. CometD is supposed to detect this failure and fall back to https, however, it does so by attampting the WebSocket connection, catching the failure, and re-trying with https. Very inefficient and also means that the example code from the Streaming API Developer Guide will not work as is. There are two fixes to make sure you have a robust connection, only one is required but the combination builds in some efficiency and reliability so I highly reccoment implementing both fixes.

    Fix 1

    Add the line

    $.cometd.websocketEnabled = false;

    before calling $.cometd.init to disable WebSockets right off the bat. This will prevent CometD from even attempting the pointless WebSockets call.

    Fix 2

    Do your subscribes only after the handshake has succeeded by using callbacks instead of syncronous calls like the Salesforce documented example has you do. To do this, you need to split the legacy init call into configure and handshake with a callback.

    • $.comet.init should simply change to $.cometd.configure, all parameters can stay exactly as they are.
    • Wrap your subscribe call the handshake callback
     $.cometd.handshake(function(handshakeReply) { 
        if(handshakeReply.successful) { 
          SUBSCRIBE GOES HERE 
        }
      });

    Problem 4: VisualForce overrides XMLHttpRequest with IServerXMLHTTPRequest2 in IE only

    This is a problem because IServerXMLHTTPRequest2 is missing attributes used by the jQuery $.ajax calls.

    This one took me forever to find as I had to first use IE debugger with breakpoints to even find out that the object substitution was taking place and then IServerXMLHTTPRequest2 + anything in a search engine returns no results. Finally, I found this forum post. Luckily the fix is easy, just add the following code block before making any $.CometD calls

    /* Override the jquery xhr/XMLHttpRequest generation
        This is needed to support IE 11 in VisualForce due to Salesforce 
        overriding XMLHttpRequest and breaking the ajax call
        SOURCE: https://developer.salesforce.com/forums/?id=906F0000000AjLtIAK
        */
      $.ajaxSetup({
        xhr: function() {
          try {
            var request = null;
            if(Sarissa.originalXMLHttpRequest) {
                request = new Sarissa.originalXMLHttpRequest();
            } else if (window.XMLHttpRequest) {
                request = new XMLHttpRequest();
            }
            return request;
          } catch ( e ) {}
        }
      });

    Putting it all together

    Lets take the Salesforce example

    $(document).ready(function() {
        // Connect to the CometD endpoint
        $.cometd.init({
           url: window.location.protocol+'//'+window.location.hostname+'/cometd/24.0/',
           requestHeaders: { Authorization: 'OAuth {!$Api.Session_ID}'}
       });
    
       // Subscribe to a topic. JSON-encoded update will be returned
       // in the callback
       $.cometd.subscribe('/topic/InvoiceStatementUpdates', function(message) {
           $('#content').append('<p>Notification: ' +
                'Channel: ' + JSON.stringify(message.channel) + '<br>' +
                'Record name: ' + JSON.stringify(message.data.sobject.Name) +
                '<br>' + 'ID: ' + JSON.stringify(message.data.sobject.Id) + 
                '<br>' + 'Event type: ' + JSON.stringify(message.data.event.type)+
                '<br>' + 'Created: ' + JSON.stringify(message.data.event.createdDate) + 
                '</p>');
        });
    });

    This would become

    $(document).ready(function() {
      /* Override the jquery xhr/XMLHttpRequest generation
          This is needed to support IE 11 in VisualForce due to Salesforce 
          overriding XMLHttpRequest and breaking the ajax call
          SOURCE: https://developer.salesforce.com/forums/?id=906F0000000AjLtIAK
          */
        $.ajaxSetup({
          xhr: function() {
            try {
              var request = null;
              if(Sarissa.originalXMLHttpRequest) {
                  request = new Sarissa.originalXMLHttpRequest();
              } else if (window.XMLHttpRequest) {
                  request = new XMLHttpRequest();
              }
              return request;
            } catch ( e ) {}
          }
        });
    
        //Don't use WebSockets as they aren't supported
        $.cometd.websocketEnabled = false;
    
        // Configure to the CometD endpoint
        $.cometd.configure({
           url: window.location.protocol+'//'+window.location.hostname+'/cometd/24.0/',
           requestHeaders: { Authorization: 'OAuth {!$Api.Session_ID}'}
        });
    
        // Subscribe in the handshake callback only if successfull
        $.cometd.handshake(function(handshakeReply) { 
          if(handshakeReply.successful) { 
            $.cometd.subscribe('/topic/InvoiceStatementUpdates', function(message) {
              $('#content').append('<p>Notification: ' +
                  'Channel: ' + JSON.stringify(message.channel) + '<br>' +
                  'Record name: ' + JSON.stringify(message.data.sobject.Name) +
                  '<br>' + 'ID: ' + JSON.stringify(message.data.sobject.Id) + 
                  '<br>' + 'Event type: ' + JSON.stringify(message.data.event.type)+
                  '<br>' + 'Created: ' + JSON.stringify(message.data.event.createdDate) + 
                  '</p>');
            });
          }
        });
    });

    I hope this saves some of you some huge headaches when using the Streaming API with IE11.

    #blog #mavens

  • Why We Need A Builder Pattern For Our Test Data

    Why we need a Builder Pattern for our test data

    Properly testing Apex code is a necessary and important element of Salesforce development. To do so requires building test data that grows in complexity with the code you are testing.

    The Force.com platform gives us a pretty simple way to create sObject Data for Unit Tests. With the dynamic constructors that sObjects are equipped with, we can create records with populated fields.

    How is it then that so many people struggle to create good test data?

    This pattern does not hide the complexity of the test data creation from the actual test method / class:

    Order__c someOrder = new Order__c(
    	OrderNumber__c = '12345', 
    	OrderSummary__c = 'Some Summary'
    );
    insert someOrder;
    
    OrderItem__c someItem = new OrderItem(
    	Order__c = someOrder.Id, 
    	Item__c = 'Table'
    );
    insert someItem;
    
    ShippingAddress__c someAddress = new ShippingAddress__c(
    	Order__c = someOrder.Id, 
    	Street__c = 'Some Street', 
    	City__c = 'Some City', 
    	State__c = 'Some State', 
    	Country__c = 'Some Country'
    );
    insert someAddress;

    Now, there are several issues with the above pattern:

    • First off, we need to know all this information for every test method or class we want to create this data for.
    • We also need to know which fields need to be populated for our initial insert.
    • As well as which fields we need for our actual test.

    So at some point, we start to abstract this knowledge away to make our tests clearer and more readable.

    This is where the Factory Pattern comes in…

    The Factory Pattern is used inside the Force.com community to create abstract test data in an easy, convenient and re-usable way. For example, the above test data could be generated by one line:

    TestDataFactory.createSomeOrder();

    Seems nice enough, right?

    It is.

    But now let’s take this pattern one step further. Let’s create a more specific test data:

    • [ ] an order with specific items and specific addresses. What do we do?
    TestDataFactory.createSomeOrder();
    TestDataFactory.createTableAndChairsOrder();
    TestDataFactory.createTableAndChairsOrderWithGermanShippingAddress();
    TestDataFactory.createChairsAndLampsOrder();

    Over time, our factory classes tend to get bloated, messy and very hard to maintain to a high coding standard.

    In the example above, what if we want to create an order with tables, chairs and lamps with a generic address? -> We again create a new static method with specific naming. What happens when our test breaks? And how do we find out quickly which value the OrderSummary__c field has for a specific test? -> It is likely we have to dig into the factory class itself to find out. What if we need to change this value just for one of our new tests?

    This is an awful state for our code. The harder it is to understand unit tests, the slower it is to update them and write new ones. This leads to poorly tested code and projects that fail to meet deadlines and requirements.

    It feels like our tests drag us down and slow us down.

    How can we avoid this terrible state?

    In Part Two of our Builder Pattern blog series, we’ll see how the Builder Pattern can clean up some of the issues outlined above.

    #blog #builderPattern #mavens

  • A Quick Tour of the Lightning Design System

    On March 2nd, version 1.0.0 of the Lightning Design System was released. Seemed to us like a great time to take a quick tour of the system and a few of the core features available.

    Read more
  • Streamlining Application Deployment with Heroku and Contentful

    Speed up Deployment

    We recently updated an application for a client and helped dramatically decrease the time it takes to deploy a change. It was taking upwards to 2 weeks to get through a full deployment the application and now a change can be made and deployed in a matter of minutes.

    Read more
  • Create a Lightning Documentation Bookmarklet

    What Problem is Being Solved?

    Salesforce Lightning is a great platform and one of the cool “features” is that it encourages the developers to document their Lightning components using the tags. The platform will then dynamically build documentation specific to your org.

    One minor gripe I have is needing to remember the path to the documentation. Read more

  • Dynamically Select Databases in Rails!

    Background

    You may be familiar with having a model connect to a non-default database, which can be handled by something like:

    class AlternateUser < ActiveRecord::Base
      establish_connection "#{Rails.env}_alternate".to_sym
      ...
    end
    Read more
  • React Native From the Trenches

    At the start of last year Facebook announced React Native, a new mobile development framework that would enable developers to build cross platform applications using JavaScript which would perform as well as an app written natively. You can watch the announcement video here.

    Read more
  • New Year, New Blog - Announcing mavens.github.io

    Firstly Happy New Year from everyone at Mavens! We wish you all a very prosperous and Happy New Year full of opportunities.

    Read more

subscribe via RSS