Pages

Sunday, 31 January 2016

Freezing Users with Lightning Components

Freezing Users with Lightning Components

Freezer

Introduction

Back in November 2013 I wrote a blog post about a new feature in the Winter 14 release of Salesforce - freezing users. At the time this was only available via the API, so I came up with a Visualforce solution that leveraged the Ajax Toolkit. This post presents the Lightning Components equivalent.

Screen Shot 2016 01 31 at 08 29 11

One Does Not Simply Call the API

Not from a Lightning Component, at least. The docs make this quite clear: 

"Make API calls from an Apex controller. You can’t make API calls from JavaScript code."

Luckily the sObject that encapsulates the frozen status of a user, UserLogin, is now available and updatable in Apex, so the Lightning Component will be responsible for allowing an admin to choose which users to freeze/unfreeze, while the actual database changes will be handled by its Apex controller.

TL;DR

Boring

The full code is available at the following Gists:

The Lightning Component

The key part of the Lightning Component is the iterator that outputs user information and a checkbox so that the user’s frozen status can be updated. This makes use of a wrapper class that encapsulates the User and UserLogin sObjects for a specific user : 

<aura:iteration items="{!v.UserDetails}" var="ud">
  <tr>
    <td>{!ud.user.FirstName}</td>
    <td>{!ud.user.LastName}</td>
    <td>{!ud.user.Username}</td>
    <td id="{!ud.user.Id}">
      <ui:inputCheckbox change="{!c.frozenChanged}"
                        value="{!ud.userLogin.IsFrozen}" />
    </td>
  </tr>
</aura:iteration>

Note the input checkbox has a handler for when the value is changed - I found I couldn’t just bind the value from the underlying UserLogin field. I suspect this is because I am binding to a field from a complex object rather than directly to a primitive. This meant that I had to fire an event when the value changed, then figure out which user the checkbox was associated with. Ideally I’d have given the inputCheckboxComponent an aura Id that included the user id, but the aura Id for this component must be a string literal, so if I use something like aura:id=“{!’cb-‘ + ud.user.Id}”, then the aura id generated is exactly that - {!’cb-‘ + ud.user.Id}, which isn’t very useful.

Where I can use a dynamically generated id is in the containing element - the table cell in this case. My event handler that is invoked when the value is changed can then use the id of the parent element to determine the user id, and invert the current value of the IsFrozen field:

updateCheckbox : function(cmp, ev) {
    var value=ev.getSource().get('v.value');
    var userId=ev.getSource().getElement().parentElement.id;
    var userDetails=cmp.get('v.UserDetails');
        
    for (var idx=0; idx<userDetails.length; idx++) {
        if (userDetails[idx].user.Id==userId) {
            userDetails[idx].userLogin.IsFrozen=!value;
        }
    }
    cmp.set('v.UserDetails', userDetails);
}

The final line once again demonstrates the power of Lightning Components - by updating the component attribute containing the wrapper classes, the component rerenders the user information table with the latest details.

When the changes have been made on the client side, the collection of wrapper classes is then sent back to the server so that the UserLogin records can be updated. Even though I know there is a bug around sending an array as a parameter to the Apex controller, I still spent 15 minutes figuring out why doing exactly this threw an internal server error! Once I remembered I used the now familiar workaround, converting the collection of wrapper classes to a JSON string:

var userDetails=cmp.get('v.UserDetails');
var udAsJSON=JSON.stringify(userDetails);
var params={
          "toProcessAsJSON" : udAsJSON
};

 and turning this back into the wrapper class collection server side:

Type udArrType=Type.forName('List<UserFreezer.UserDetails>');
List<UserDetails> toProcess =
    (List<UserDetails>)JSON.deserialize(toProcessAsJSON, udArrType);

Related Posts

  

Sunday, 24 January 2016

Apex Test Suites in Spring 16

Apex Test Suites in Spring 16

(Note that this blog post refers to functionality that is intended to be made available in the Spring 16 release of Salesforce, but features may be removed prior to the Spring 16 go-live date, so you may never see this in the wild!)

Introduction

One of the features that I missed most when moving from Java to Apex was the ability to create test suites. A suite is a container for a group of related tests, and is typically used to test a specific area of functionality. A common use case for me is around integration - when I’m making wide ranging changes I’m always keen to make sure I haven’t affected any of the code that integrates with downstream systems. Without test suites its either a case of manually executing individual tests and hoping I remember all of the tests I need, or running all tests and in some cases (e.g. our BrightMedia product) waiting 45 minutes for then all to complete.

While it has been possible to do this outside of the Force.com UI, there were compromises.  Using the Force CLI or Force.com Migration Tool (ant) meant that I had to execute my test suite from my laptop, while using the ApexTestQueueItem object meant that I either had to write code to create a new suite or install a package that allowed me to manage suites through a custom Visualforce page.The one thing all of my solutions had in common was a lack of flexibility to allow me to create a test suite and execute it on  any org at any time from any machine.

This all changes with the Spring 16 release and the introduction of test suites on the Salesforce Platform.

Using Test Suites

The easiest way to use test suites is via the Developer Console. I’m not the biggest fan of this from the perspective of authoring code, but its pretty much my go to tool now for running unit tests, mainly because it runs in the browser so I can execute tests on any org regardless of which device I’m using. 

In Spring 16 there are some new options in the Test menu for suites:

Screen Shot 2016 01 24 at 15 19 16

New Suite, as the name suggests allows you to create a new test suite. After specifying the suite name, you can then choose which classes make up the suite:

Screen Shot 2016 01 24 at 15 23 31

Note that you can’t specify individual test methods in a suite, so all methods in the class will be run when the suite is executed.

Suite Manager also allows you to create a new suite, or to edit (change the classes) or delete an existing suite.

New Suite Run allows you to execute a suite of tests. A nice feature for this is that it doesn’t limit you to a single test suite - you can choose multiple suites to run, creating a suite of suites if you will:

Screen Shot 2016 01 24 at 15 32 19

Note the settings button - this gives you access to another new feature in Spring 16 - automatically terminating a test run after a certain amount of failures. Specify the maximum number of failures through the resulting dialog:

Screen Shot 2016 01 24 at 15 34 35

As the number of failures allowed is 5, if 6 test cases fail, then test run will be automatically terminated. A synchronous test run terminates immediately, while an asynchronous test run waits until the currently executing test cases have finished and terminates once that is the case.

Once the test suite completes, the results are displayed as usual:

Screen Shot 2016 01 24 at 15 45 26

You can also run test suites via the API or version 36 the Force.com IDE, although bear in mind that if you upgrade to this version of the IDE you won’t be able to use it to work on any org that hasn’t been upgraded to Spring 16.

In a perfect world I’d like to see the following added to test suites:

  • Nesting suites - this would allow me to use my suites as building blocks, to assemble a super-suite from a set of component suites, or to extend (but not replace) an existing suite with one or two classes
  • Specify methods to execute in a suite - a bit more of an edge case, but this would allow me to create suites for particular aspects of a group of test classes - e.g. just the processing triggered when various types of sobject are inserted

Neither of these are a big deal, but one thing is a constant in software development and that is that everyone is a critic!

Related Posts

Saturday, 16 January 2016

Board Anything with SLDS and Lightning Components

Board Anything with SLDS and Lightning Components

Ba meme

Introduction

One of the features I really like in the Lightning Experience is the Opportunity Board (or Opportunity Kanban as it will be known in Spring 16). Not so much the ability to drag and drop to change the stage of an individual opportunity, although that’s very useful for the BrightGen Sales team, but more the visualisation of which state a number of records are in.

This seemed to me something that would useful for a wide range of records - anything with a status-like field really, which lead me to create Board Anything - a Lightning component that retrieves records for a particular SObject type, groups them by the value in a configurable status field and displays them as a table of Salesforce Lighting Design System board tiles - Leads for example:

Screen Shot 2016 01 16 at 17 10 36

Whatever, Where’s The Code

The Board Lightning Component

The board contents are displayed via a Lightning Component. In order for this component to be able to handle any type of sobject, there are a few attributes to configure its behaviour:

<aura:attribute name="SObjectType" type="String" default="Case" />
<aura:attribute name="StageValueField" type="String" default="Status" />
<aura:attribute name="StageConfigField" type="String" />
<aura:attribute name="FieldNames" type="String" default="CaseNumber,Subject" />
<aura:attribute name="ExcludeValues" type="String" default="Closed" />

These attributes influence the component as follows:

  • SObjectType - the type of SObject to display on the board
  • StageValueField - the field containing the stage (or status) values
  • StageConfigField - a picklist field containing the values to display on the board - use this if your StageValueField is a free text field to limit the stages that are displayed on the board
  • FieldNames - comma separated list of fields to display in each tile - the first is used as the title field for the tile
  • ExcludeValues - stage values that should be excluded from the board

Note that the defaults will result in a board of open cases being displayed as follows:

Screen Shot 2016 01 16 at 17 24 05

The component also defines design attributes for each of these attributes, so that you can configure a board via the Lightning Builder.

The component does most of its work on a list of wrapper classes. First it iterates the list  to output the stage headings, which will be in the order returned by the Schema Describe methods for the stage value field or stage config field, depending on which is defined:

 
<aura:iteration items="{!v.Stages}" var="stage">
  <th style="{!'width:' + v.ColumnWidth + '%'}">
     <h3 class="slds-section-title--divider slds-text-align--center">{!stage.stageName}</h3>
  </th>
</aura:iteration>

and then iterates the list again, displaying the tiles for each record in each stage:

<aura:iteration items="{!v.Stages}" var="stage">
  <td style="{!'width:' + v.ColumnWidth + '%; vertical-align:top'}">
    <aura:iteration items="{!stage.sobjects}" var="sobject">
      <ul class="slds-list--vertical slds-has-cards--space has-selections">
        <li class="slds-list__item slds-m-around--x-small">
          <div class="slds-tile slds-tile--board">
            <p class="slds-tile__title slds-truncate slds-text-heading--medium">{!sobject.titleField.fieldValue}</p>
            <div class="slds-tile__detail">
              <aura:iteration items="{!sobject.fields}" var="field">
                <p class="slds-truncate">{!field.fieldName} : {!field.fieldValue}</p>
              </aura:iteration>
            </div>
          </div>
        </li>
      </ul>
    </aura:iteration>
  </td>
</aura:iteration>

The Apex Controller

Most of the heavy lifting is done by the Apex controller, which gets the available stage values for the stage value field or stage config field:

if ( (null==stageConfigField))
{
  stageConfigField=stageValueField;
}
            
Map<String, String> excludeValuesMap=new Map<String, String>();
if (null!=excludeValues)
{
  for (String excludeValue : excludeValues.split(','))
  {
    excludeValuesMap.put(excludeValue, excludeValue);
  }
}
Map<String, BB_LTG_BoardStage> stagesByName=new Map<String, BB_LTG_BoardStage>();

Map<String, Schema.SObjectField> fieldMap =
             Schema.getGlobalDescribe().get(sobjectType).
		getDescribe().fields.getMap();
Schema.DescribeFieldResult fieldRes=
             fieldMap.get(stageConfigField).getDescribe();
            
List<Schema.PicklistEntry> ples = fieldRes.getPicklistValues();
for (Schema.PicklistEntry ple : ples)
{
  String stageName=ple.GetLabel();
  if (null==excludeValuesMap.get(stageName))
  {
    BB_LTG_BoardStage stg=new BB_LTG_BoardStage();
    stg.stageName=ple.GetLabel();
    stagesByName.put(stg.stageName, stg);
    stages.add(stg);
  }
}

Note that BB_LTG_BoardStage wrapper classes are created here - later these will be fleshed out with the records and their details.

It then generates a dynamic SOQL query to retrieve the named fields:

String queryStr='select Id,' + String.escapeSingleQuotes(stageValueField) + ', ' +
String.escapeSingleQuotes(fieldNames) +
                ' from ' + String.escapeSingleQuotes(sobjectType);
List<SObject> sobjects=Database.query(queryStr);

 and finally iterates the results of the query, grouping the records by stage and using dynamic DML to retrieve the field values: 

List<String> fieldNamesList=fieldNames.split(',');
for (SObject sobj : sobjects)
{
  String value=String.valueOf(sobj.get(stageValueField));
  BB_LTG_BoardStage stg=stagesByName.get(value);
  if (null!=stg)
  {
    BB_LTG_BoardStage.StageSObject sso=new
    BB_LTG_BoardStage.StageSObject();
    Integer idx=0;
    for (String fieldName : fieldNamesList)
    {
      fieldName=fieldName.trim();
      fieldRes= fieldMap.get(fieldName).getDescribe();
      BB_LTG_BoardStage.StageSObjectField ssoField=
                      new BB_LTG_BoardStage.StageSObjectField(fieldRes.getLabel(),
             sobj.get(fieldName));
      if (0==idx)
      {
        sso.titleField=ssoField;
      }
      else
      {
        sso.fields.add(ssoField);
      }
      idx++;
    }
    stg.sobjects.add(sso);
  }
}

What About the Example Leads Board?

The example Leads board is built using Lighting Out for Visualforce. As you may remember from my previous blog post on this, first I need to create an app that references the component, in this case BBSObjectBoardOutApp:

<aura:application access="GLOBAL" extends="ltng:outApp">
    <aura:dependency resource="c:BBSObjectBoard" />
</aura:application>

And then I have a Visualforce page (BBLeadBoard) that uses the app to instantiate the component and sets up the attributes to configure the board for leads:

<apex:page sidebar="false" showHeader="false" standardStylesheets="false">
    <apex:includeScript value="/lightning/lightning.out.js" />
    <div id="lightning"/>
 
    <script>
        $Lightning.use("c:BBSObjectBoardOutApp", function() {
            $Lightning.createComponent("c:BBSObjectBoard",
                                       {
					'SObjectType': 'Lead',
					'StageValueField' : 'Status',
					'FieldNames' : 'Company, FirstName, LastName, LeadSource',
					'ExcludeValues': 'Converted',
					},
                  "lightning",
                  function(cmp) {
                    // no further setup required - yet!
              });
        });
    </script>
</apex:page>

That’s All There Is?

No, there’s the wrapper class, a JavaScript controller and helper, but there’s nothing particularly exciting about those so they are available from my BBLDS samples repository at:

https://github.com/keirbowden/BBLDS

This also contains an unmanaged package so you can just install this into your dev org and try out the Visualforce page - check out the README for more information.

Anything Else I Need to Know

Yes.

  • As this is an unmanaged package there are test classes - you’re welcome.
  • The styling of the headings could be improved
  • Errors are written to the debug log and swallowed server side - I did this as otherwise the Lightning Builder displays exceptions all over the place when you try to update the attributes. Its not ideal, so feel free to change it!
  • No drag and drop support to change the stage/status value - while this makes sense for opportunities, it doesn’t for things like cases, so I used that as justification not to do it.

Related Posts

 

Sunday, 3 January 2016

Salesforce Spring 16 Release - CreatedDate and Apex Unit Tests

Createddate

(Note that this blog post refers to functionality that is intended to be made available in the Spring 16 release of Salesforce, but features may be removed prior to the Spring 16 go-live date, so you may never see this in the wild!)

Introduction

Anyone who has written a reasonable amount of Apex unit tests is likely to have butted up against the shortcoming that the CreatedDate is set when a record is inserted into the Salesforce database. While at first glance this may not seem to be such a huge issue, consider the scenario of a custom Visualforce page, maybe for use in a dashboard, that displays the 10 most recent cases:

Screen Shot 2016 01 03 at 14 58 50

The controller is about as straightforward as it can be - simply querying back the records from the Salesforce database:

public class RecentCasesController
{
    public List<Case> getRecentCases()
    {
        return [select id, CaseNumber, Subject, Account.Name, Contact.Name,
                CreatedDate
                from Case
                order by CreatedDate desc
                limit 10];
    }
}

while the page is equally straightforward, simply iterating the records in a pageblocktable:

<apex:page controller="RecentCasesController">
	<apex:pageBlock title="10 Most Recent Cases">
        <apex:pageBlockTable value="{!recentCases}" var="cs">
            <apex:column value="{!cs.subject}"/>
            <apex:column value="{!cs.Account.Name}" />
            <apex:column value="{!cs.Contact.Name}" />
            <apex:column value="{!cs.CreatedDate}" />
        </apex:pageBlockTable>
    </apex:pageBlock>
</apex:page>

A Testing Problem

Testing this in the Winter 16 release (and earlier), testing this functionality meant a compromise, such as:

  • Inserting a number of records and ensuring that only 10 of them were returned, but being unable to predict which 10 this would be. This is because the CreatedDate field has second granularity, and if there are a number of records inserted in the same second, there is no guarantee of the order they will be returned in when the query is ordered by the CreatedDate. The Salesforce database will do the least amount of work to execute the query, and there’s nothing you can do to affect it. From a unit testing perspective, unless you can 100% guarantee the order of the results, asserting for a particular order makes a test fragile.
  • Adding an autonumber field to the case sobject so that you can rely on that to guarantee the ordering. This is a classic example of changing your implementation to satisfy your unit tests, which if nothing else is annoying to have to resort to.
  • Trying to kill some time to allow the second to tick over in between record inserts, by looping a lot and generating debug statements for example. There are a couple of problems with this approach. First, it is fragile, as you have no control over how much time would be burned this way. All you can do is hope that the performance you have seen in the past continues, which can’t give much confidence. More importantly, it is evil, as you are consuming shared resources for no good reason.
  • Provide some form of dependency injection, probably via a @TestVisible private property, to allow your test to inject the list of cases that should be returned as a result. This mechanism means that all you are really testing is that the controller returns the list of cases you asked it to, bypassing the query that will be executed in production. Writing code that solely allows your tests to complete successfully is usually an indication that something is awry.

One DOES Simply Set the CreatedDate

In the Spring 16 release this is all set to change (although it may not - see the note at the top of this blog). The new Test.setCreatedDate(Id, DateTime) method allows you to change the CreatedDate once a record is inserted.

For my recent cases page, I can now write a test case to verify that only the 10 most recent cases are returned, by setting the CreatedDate for each case record after I’ve inserted them in the database:

@isTest
private class RecentCasesController_Test
{
    @isTest
    static void TestGetRecentCases()
    {
        /* setup */
        Account acc=new Account(Name='Unit Test');
        insert acc;
        
        Contact cont=new Contact(FirstName='Unit',
                                 LastName='Tester');
        insert cont;
        
        List<Case> cases=new List<Case>();
        for (Integer idx=1; idx<=20; idx++)
        {
            Case cs=new Case(Subject='Test Case ' + idx,
                             AccountId=acc.Id,
                             ContactId=cont.Id);
            cases.add(cs);
        }
                             
        insert cases;
        
        // now set the created date for each case - the first case
        // in the list will be the most recent, the second case the
        // second most recent, and so on
        for (Integer idx=1; idx<=20; idx++)
        {
            DateTime created=System.now().addDays(-(idx*10));
            Test.setCreatedDate(cases[idx-1].Id, created);
        }
	
        /* execute */
        RecentCasesController ctrl=new RecentCasesController();
        List<Case> recentCases=ctrl.getRecentCases();

        /* verify */
 
        // should be 10 cases
        System.assertEquals(10, recentCases.size());
        
        // should be the first 10 inserted, in that order
        for (Integer idx=0; idx<10; idx++)
        {
            System.assertEquals(recentCases[idx].Id, cases[idx].Id);
        }
    }
}

Final Thoughts

The ability to set the created date is a much needed addition to the Salesforce platform testing framework, and one that will allow production code to be properly tested, and allow some workarounds/terrible hacks to be retired. 

It would be cool to be able to set the CreatedDate when constructing the sObject, prior to insertion, but I’d imagine this would require a ton of changes to the Salesforce database layer and isn’t going to happen. I foresee thousands of test fixture classes that insert a record and then change its CreatedDate!

Its a shame that at the moment you can only set the CreatedDate of a single record per call, but hopefully we’ll get some bulk capability in the future. The Salesforce platform approach is typically to make things doably difficult, then add the bells and whistles.

Finally, the documentation doesn’t mention any effects on limits and my testing with the code above bears this out, which is always good.

Related Posts