Sunday, 15 May 2022

The CPU Effects of Sorting

 


Introduction

Regular readers of this blog will know that I'm curious about the CPU time consumed by various developer activities on the Salesforce platform - as I've written before, developing at enterprise scale often turns into a Man Vs CPU battle. One area I've been meaning to look into for a while is list sorting, and it's been an interesting investigation.

Sorting Lists

Sorting sounds quite simple - if it's a list of primitives then you can probably use the built-in List.sort() method. If not then you'll likely want to implement the Comparable interface, which requires a single method:

    Integer compareTo(Object compareTo)

This method compares the current object instance to the compareTo object instance and returns +1 if this instance is the larger, -1 if it is the smaller and 0 if both instances are the same. How you determine the result depends on your specific business logic, but can also have a significant effect on your request's CPU consumption, as the following scenarios will show. To capture the CPU consumed, in each scenario I created a list with 10,000 elements using Math.random() to ensure as little ordering as possible in the initial creation. I then sorted this list, capturing the CPU consumed before and afterwards, with the log level turned right down. I wouldn't take too much notice of the exact values, but the key point is the difference between them.  If you are interested in the exact implementation, click the link under each scenario title to see the code in the Github repository.

Primitive Sort

PrimitiveSort.cls 

The baseline to compare against - this consumed 20 milliseconds of CPU.

Single Property

SimpleComparableSort.cls

A custom object containing a single Integer property and a compareTo method that accessed this property directly on both instances. This consumed 336 milliseconds of CPU - a significant increase to access a single property.

Multiple Properties Combined

MultiComparableSort.cls

A custom object containing two Integer properties and a compareTo method that accesses the properties directly, and multiplies them together before comparing.  This consumed 865 milliseconds of CPU - another significant increase.

Multiple Properties Combined and Calculated in Advance

MultiComparableCalcSort.cls

A custom object containing two Integer properties that multiplies them together and stores them in a third property which is used directly in the compareTo method. This is an iteration on the MultiComparableSort custom object scenario, and consumed 352 milliseconds, showing that it can be worth doing some additional work in advance on the objects you are going to sort.

Method Call

MethodComparableSort.cls

A custom object containing an Integer property that is not public, requiring a method to be executed to retrieve the value for the instance being compared to. This consumed 773 milliseconds of CPU time, an increase of over 100% from accessing the property directly. 

Creating a public property containing a read only copy of the value for sorting purposes would change this back to the performance of the SimpleComparableSort class, bringing it back to 336 milliseconds. A slight change to the object, but a big saving.

Multiple Methods Called and the Results Combined

MultiMethodComparableSort.cls

A custom object containing two private properties which must be multiplied together to use in the compareTo method. In this case there are two methods called every time compareTo is executed, pushing the CPU up to 1081 milliseconds Again, this could be improved by creating public read-only properties exposing the values, or calculating them in advance and storing them in a single public property.

SObject Comparing Two Fields

SObjectComparableSort.cls

A more complex scenario - a list of custom objects containing an Opportunity sObject that require a two step comparison - first the stages are compared and if they are identical, the amounts are then checked. The stage names are stored in a list and the position in the list for each object determined using the indexOf method. This consumed 3428 milliseconds of CPU - over 1/3 of that available to the entire transaction.

SObject Comparing Two Fields - Calculate Stage Value

SObjectComparableIndexSort.cls

An iteration on the previous scenario - calculating the index for the stage when the custom object is constructed, rather than determining it on the fly in the compareTo method. This consumed 1911 milliseconds of CPU time. An improvement of just under 50% for very little effort.

SObject Comparing Two Fields - Calculate Value for Sorting

SObjectComparableCalcSort.cls

A further iteration on the previous scenario - calculating a unique value to represent the stage and the amount. This might not be a practical approach in many real world cases, but it's something to think about if you encounter problems. I've decided that my maximum opportunity value is five million, so I get the stage index and multiply it by one billion, then add on the opportunity amount. This allows me to revert back to sorting on a single property, and unsurprisingly brings the CPU consumed down to 349 milliseconds.


Understanding and Mitigating the CPU Impact

The reason for the significant increases with what feels like small increases in complexity is down to the sheer number of times that the compareTo method will be invoked on a decent sized list. It's easy to get into a mindset that this method is called once per object, or maybe a couple of times, but the truth is very different. In the scenarios above with a 10,000 item list, compareTo was called over 120,000 times, so the impact of adding a method call, accessing another property, or checking against multiple properties scales up really fast. If you are interested in reading more about why this is, check out Insertion sort or Selection sort for some walkthroughs of what actually happens during a sort.

The simplest way to mitigate this is to move as much work out of the compareTo method and do it up front, either at construction time or as a pre-sort activity. As an example from my scenarios above, replacing a method call with a public property requires 10,000 executions of the code to set up the property, rather than 120,000 executions of the method to access the private value. The real sweet spot is if you can generate a primitive that represents the ordinal value of the object, as sorting on that is far more efficient than comparing various aspects of the object.

Conclusion

The key takeaway here, as usual, is to think about the effect of the code that you are writing. If you are implementing Comparable, take a few minutes to profile the code with lists of reasonable size and see if you can do anything to reduce the impact. Most Salesforce instances start out simple with small data volumes, but it's remarkable how quickly the amount of data will scale and you need to make sure your code scales with it.

Related



Saturday, 2 April 2022

The Org Documentor Keeps On Executing

 


Introduction

Back in February I added support for some of the steps of the order of execution, mostly because of the flow ordering support added in Spring 22. This has created a nice backlog of work to support more of the steps, starting with duplicate rules which I added in today. 

Duplicate Rules

This is a slight departure from earlier releases of the documentor, in that I haven't added processing of duplicate rules to generate a dedicated page, I've just added them to the order of execution page. If you think they need their own page, or more likely their own section in the object detail pages, please raise a request in the Github repository and I'll see what I can do.


The order of execution page lists the active duplicate rules and the matching rules that they depend on. I'm undecided as to whether any more information is needed, but again if you think there is, please feel free to raise an issue in the repo.


As always, you can see an updated example of the order of execution, and the other pages, generated from the sample metadata at the Heroku site.

Updated Plug-in


Version 4.0.5 of the plug-in has this new functionality and can be found on NPM

If you already have the plug-in installed, just run sfdx plugins:update to upgrade to 4.0.5 - run sfdx plugins once you have done that to check the version.

The source code for the plug-in can be found in the Github repository.

Columbo Close


Just one more thing, not related to the Documentor itself but the Google site that I use to document it. This now has it's own custom domain - orgdoc.bobbuzzard.org. With a small amount of DNS changes to apply the custom domain to the site, Google provides the SSL certificate for me, which is nice.

Related Posts



Sunday, 27 February 2022

Org Documentor - (Some of) The Order of Execution

Introduction

It's been a while since I made any changes to the Org Documentor, partly because I've been focused in other areas, and partly because I didn't need anything else documented. This changed with the Spring 22 release of Salesforce and the Flow Trigger Explorer

I really liked the idea of the explorer, but was disappointed that it showed inactive flows and didn't reflect the new ordering capabilities. Why didn't they add that, I wondered. How hard could it be? Then it occurred to me that I could handle this myself through the Org Documentor. It turns out I couldn't handle all aspects, but still enough to be useful. More on that later.

Flow Support

Up until now I hadn't got around to including flows in the generated documentation, and this clearly needed to change if I wanted to output the order they were executed in. 

As long as API version 54 is used, the execution order information comes back as expected, and getting these in the right order and handling collisions based on names is straightforward with a custom comparator function. Sadly I can't figure out the order when there is no execution information defined, as CreatedDate isn't available in the metadata. Two out of three ain't bad.

Order of Execution

As there are multiple steps in the order of execution, and most of those steps require different metadata, I couldn't handle it like I do other metadata. Simply processing the contents of a directory might help for one or two steps, but I wanted the consolidated view. To deal with this I create an order of execution data structure for each object that appears in the metadata, and gradually flesh this out as I process the various other types of metadata. So the objects add the validation rule information, triggers populate the before and after steps, as do flows. 

As everyone knows, there's a lot of steps in the order of execution, and I'm not attempting to support all of them right now. Especially as some of them (write to database but don't commit) don't have anything that metadata influences! Rather than trying to detail this in a blog post that I'd have to update every time I change anything, the order of execution page contains all the steps and adds badges to show which are possible to support, and which are supported:


As you can see, at the time of writing it's triggers and flows plus validation rules and roll up summaries. 

Steps where there is metadata that influences the behaviour appear in bold with a badge to indicate how many items there are. Clicking on the step takes you to the section that details the metadata:


You can see an example of the order of execution generated from the sample metadata at the Heroku site.

Updated Plug-in


Version 4.0.1 of the plug-in has this new functionality and can be found on NPM

If you already have the plug-in installed, just run sfdx plugins:update to upgrade to 4.0.1 - run sfdx plugins once you have done that to check the version.

The source code for the plug-in can be found in the Github repository.

Related Posts



Saturday, 19 February 2022

Lightning Web Component Getters

Introduction

When Lightning Web Components were released, one feature gap to Aura components I was pleased to see was the lack of support for expressions in the HTML template. 

Aura followed the trail blazed by Visualforce in allowing this, but if not used cautiously the expressions end up polluting the HTML making it difficult to understand. Especially for those that only write HTML, or even worse are learning it. Here's a somewhat redacted version from one of my personal projects from a few years ago:

Even leaving aside the use of i and j for iterator variables, it isn't enormously clear what name and lastrow will evaluate to.

Handling Expressions in LWC

One way to handle expressions is to enhance the properties that are being used in the HTML. In the example above, I'd process the ccs elements returned from the server and wrap them in an object that provides the name and lastrow properties, then change the HTML to iterate the wrappers and bind directly to those properties. All the logic sits where it belongs, server side. 

This technique also works for non-collection properties, but I tend to avoid that where possible. As components get more complex you end up with a bunch of properties whose sole purpose is to surface some information in the HTML and a fair bit of code to manage them, blurring the actual state of the component. 

The Power of the Getter

For single property values, getters are a better solution in many cases. With a getter you don't store a value, but calculate it on demand when a method is invoked, much like properties in Apex. The template can bind to a getter in the same way it can to a property, so there's no additional connecting up required.

The real magic with getters in LWC is they react to changes in the properties that they use to calculate their value. Rather than your code having to detect a change to a genuine state property and update avalue that is bound from the HTML, when a property that is used inside a getter changes, the getter is automatically re-run and the new value calculated and used in the template.

Here's a simple example of this - I have three inputs for title, firstname and lastname, and I calculate the fullname based on those values. My JavaScript maintains the three state properties and provides a getter that creates the fullname by concatenating the values:

export default class GetterExample extends LightningElement {
    title='';
    firstname='';
    lastname='';

    titleChanged(event) {
        this.title=event.detail.value;
    }

    firstnameChanged(event) {
        this.firstname=event.detail.value;
    }

    lastnameChanged(event) {
        this.lastname=event.detail.value;
    }
    
    get fullname() {
        return this.title + ' ' + this.firstname + ' ' + this.lastname;
    }
}

and this is used in my HTML as follows:

<template>
    <lightning-card title="Getter - Concatenate Values">
        <div class="slds-var-p-around_small">
            <div>
                <lightning-input label="Title" type="text" value={title} onchange={titleChanged}></lightning-input>
            </div>
            <div>
                <lightning-input label="First Name" type="text" value={firstname} onchange={firstnameChanged}></lightning-input>
            </div>
            <div>
                <lightning-input label="Last Name" type="text" value={lastname} onchange={lastnameChanged}></lightning-input>
            </div>
            <div class="slds-var-p-top_small">
                Full name : {fullname}
            </div>
        </div>
    </lightning-card>
</template>

Note that I don't have to do anything to cause the fullname to rerender when the user supplies a title, firstname or lastname. The platform detects that those properties are used in my getter and automatically calls it when they change. This saves me loads of code compared to aura.

You can also have getters that rely on each other and the whole chain gets re-evaluated when a referenced property changes. Extending my example above to use the fullname in a sentence:

get sentence() {
    return this.fullname + ' built a lightning component';
}

and binding directly to the setter:

<div class="slds-var-p-top_small">
    Use it in a sentence : {sentence}
</div>

and as I complete the full name, the sentence is automatically calculated and rendered, even though I'm only referencing another getter that was re-evaluated:




You can find the component in my lwc-blogs repository at : https://github.com/keirbowden/lwc-blogs/tree/main/force-app/main/default/lwc/getterExample

Another area that Lightning Web Components score in is they are built on top of web standards, so if I want to change values that impact getters outside of the user interactions, I can use a regular setinterval  rather than having to wrap it inside a $A.getCallback function call, as my next sample shows:


In this case there is a countdown property that is calculated based on the timer having been started and not expiring, and an interval timer that counts down to zero and then cancels itself:

timer=30;
interval=null;

startCountdown() {
    this.interval=setInterval(() => {
        this.timer--;
        if (this.timer==0) {
           clearInterval(this.interval);
        }
    }, 1000);
}

get countdown() {
    let result='Timer expired';
    if (null==this.interval) {
        result='Timer not started';
    }
    else if (this.timer>0) {
        result=this.timer + ' seconds to go!';
    }

    return result;
}
and once again, I can just bind directly to the getter in the certainty that if the interval is populated or the timer changes, the UI will change with no further involvement from me.
<div class="slds-var-p-top_medium">
    <div class={countdownClass}>{countdown}</div>
</div>

Note that I'm also using a getter to determine the colour that the countdown information should be displayed in, removing more logic that would probably be in the view if using Aura:



You can also find this sample in the lwc-blogs repo at : https://github.com/keirbowden/lwc-blogs/tree/main/force-app/main/default/lwc/getterCountdown

Related Posts 

Sunday, 6 February 2022

Flow Collection Filter Element in Spring 22

Introduction

One of the many new flow features introduced in Spring 22 is the Collection Filter - this processes a collection applying filter conditions and creates a new collection containing only those elements that match the filter, similar to a reducer from JavaScript. This seemed like something that would save a considerable amount of effort compared to manually iterating the collection myself and applying a decision to each element. But how much effort I wondered? As Peter Drucker may have famously said, "If you can't measure it, you can't manage it", or in this case, if you can't measure it you don't know whether it is any better.

Working with Salesforce at Enterprise scale often turns into a battle of Man V CPU, so I decided to measure the impact of switching to this approach in flow, and how carrying out the same processing in Apex performed. 

The Challenge

The scenario was very (unrealistically) simple - query the Id and Forecast Category field for all opportunities from the database, create a new collection with just those in the Best Case forecast category. I used a random number generator to pick the stage for each opportunity created, and each of manual iteration, Collection Filter and Apex ran on the same set of opportunities. 

Each method of processing was run three times in no particular order to try to mitigate the impact of caching, but as usual with this kind of testing, your mileage may vary enormously depending on what else is going on.

The Results

The results were as follows:

Opportunity Count Manual Iteration Collection Filter Apex
500 480 90 20
1000 N/A 90 20
2000 N/A 116 36
3000 N/A 279 44
4000 N/A 397 65
5000 N/A 500 80
10000 N/A 850 151

One clear benefit of using the Collection Filter is the manual iteration tops out somewhere between 500 and 1000 records, breaching the number of executed elements of 2000, whereas the Collection Filter keeps going to the 10000 record limit that I imposed.

The filter approach is also about 5x more efficient in terms of CPU usage, although Apex is in turn 5x as efficient as the filter, but in all honesty if you are trying to wring every last millisecond of CPU out of a transaction involving thousands of records, flow won't be your first choice tool.

It's also good to see that the filter approach scales fairly linearly - somewhere between 80-100 msec per 1000 records, versus the 15-20 msec per 1000 records for Apex.

Related Posts


Saturday, 29 January 2022

Record Triggered Flow Ordering and Explorer in Spring 22

Introduction

As is true of most releases over the last few years, Spring 22 has a bunch of flow enhancements in it, and two particularly caught my eye. I'm pretty sure that Apex developers will be casting a longing look in their direction and wondering why the same thing isn't available for pro code, although I have some opinions about that which I'll also share!

Flow Ordering

While best practice around no, low and pro code is one <insert automation tool here> per object and action, this isn't always achievable. Even if you've been able to achieve it with your own org metadata, as soon as you install a managed package from the app exchange you'll probably get some overlap. And if you've embraced the unmanaged package development approach, you stand a good chance of undoing your own good work. 

Spring 22 introduces the concept of ordering for record triggered flows, allowing an administrator to control the order they are executed in, and an evil co-worker to cause all sorts of havoc. There might have been havoc without ordering, but this allows the evil co-worker to guarantee things run in the wrong order!

A flow trigger order value can be specified between 1 and 2000 (so much for one flow per object and action!), but it things are a little more nuanced than they might first appear.

Order values 1 - 1000 execute first, and if multiple flows have the same order value, the API name is used as a tiebreaker. 

Then flows with no order value execute, in order of their Created Date. Note that you can't change the Created Date, so if you want to influence the order of flows in this section you'll have to supply an order value and take them out of this section. I see what you did there Salesforce!

Then flows with order values 1001-2000 execute, again with the order of any collisions being decided based on the API name.

I'm not sure why those without a value run in the middle section, but obviously if you keep all your order values below 1001, then those without a value run at the end, and if you have them all above 1000 then those without a value run at the start, so you really are in full control.

Note that each of before and after save flows have their own 1-2000 set of values that are independent. You can't specify an order value that would cause an after save flow earlier than a before save flow - what a merry time of misrule that would be!

Flow Trigger Explorer

When I originally saw this I was really impressed, as I thought it was working in tandem with the new ordering functionality and showing me the exact automation in the order that it runs in. However, throwing a few examples in to show this at the January 2022 meeting of the London Salesforce Developers made me realise it's not quite as cool as that. If you look closely at the screenshot below you'll see a couple of issues if you are hoping this will show you what happens when:


First, the items in each section are sorted by flow label, not the order value, API name or Created Date. Second, inactive flows are mixed in with active ones, so you need to dig a little deeper. It does let you see at a glance the items that might impact an action on an object though, so I think it's a worthwhile start, and I'm sure that this will get more functionality as time goes on.

Why Doesn't Apex Have These?

Controlling the order that triggers run in is something that Apex developers have been looking for since forever. And finding. There are trigger handlers galore that solve this in a variety of ways, with different levels of configuration and complexity. Managed packages sometimes present a bit more of a challenge, but a well architected package will give you a way to merge their automation with yours if you need to. While I'm sure Salesforce could provide something like the order value for triggers, or indeed their own baked-in trigger handler, I'm willing to bet this would be a case of no good deed goes unpunished and those that care deeply about these things would all be upset about different aspects of the solution being forced on them. 

An Apex trigger explorer that listed the triggers involved in a single action and object would, in my view, be a solution looking for a problem. The setup pages show the triggers associated with an object, and a decent naming convention will make it clear which ones run under specific circumstances. A trigger handler framework would likely show the same trigger to rule them all for all actions for an object, or at most a dedicated trigger per action and object. 

So I don't think these are particularly needed for Apex, at least for Apex developers. Admins configuring Apex might find them useful, but with the ascent of low code tools there will probably be less of that in the future anyway. I'd still like to see some kind of unit test framework for low code though, so hopefully there's a team somewhere in Salesforce at least thinking about that.

Related



Saturday, 22 January 2022

refreshApex and Lightning Web Components

Introduction

One of the things I particularly like about Lightning Web Components is the wire service. The ability to let the platform send me the data when it decides things are ready, rather than having to figure out when to call an Apex method based on rendered callbacks and the like really cleaned up my code from the days of Aura.

Updated Records on the Server

The one area that it doesn't automatically handle for me is sending me new data when a record is updated server side, by other Apex code or maybe the controller of the LWC that is using the wire service. Initially my approach was to return the updated records to the component if it was my own controller method carrying out the update, or switch to imperative Apex calls once I knew an update had taken place. Neither of these were particularly satisfactory though.

The solution is the refreshApex method, which you import from salesforce/apex. Per the docs 

Sometimes, you know that the cache is stale. If the cache is stale, the component needs fresh data. To query the server for updated data and refresh the cache, import and call the refreshApex() function.

Exactly what I need, and it keeps my data in the scope of the wire service, so if that gets notified that there is a newer version of the data, I'll get that too.

There's a slight gotcha that makes it easy to get this wrong. When I first tried it I was convinced that it didn't work, because I'd misunderstood a key aspect of the docs. The instruction in question is:

NOTE The parameter you refresh with refreshApex() must be an object that was previously emitted by an Apex @wire.

So in my wire method handler, I captured the object that I'd retrieved:
@wire(GetPage, {name: 'home'})
gotPage(result) {
    if (result.data) {
        this.page=result.data;
        this.dataToRefresh=result.data;
    }

and when I knew the record had been updated server side, I executed the refreshApex method:

getLatest() {
refreshApex(this.dataToRefresh);
}
and nothing happened. I got no errors, but the data didn't change either. As refreshApex returns a promise, I figured maybe the issue was it wasn't resolving, so I added the code to handle success and failure:
getLatest() {
refreshApex(this.dataToRefresh)
    .then(()=> {
        this.dispatchEvent(
            new ShowToastEvent({
                title: 'Success',
                message: 'Refreshed Data',
                variant: 'success'
            })
        );
    })
    .catch((error) => {
        this.dispatchEvent(
            new ShowToastEvent({
                title: 'Error Refreshing Data',
                message: message,
                variant: 'error'
            })
        );
    });
}

this time I got an error, that didn't make a lot of sense to me, but seemed like the refreshApex method wasn't returning a promise, even though it was supposed to return an promise.

As it was the first time I'd used it, I had no idea what the correct usage looked like, so there was a bit of trial and error before I realised that "an object that was previously emitted by an Apex @wire" meant the entire object rather than the data property that I was extracting. And reading further on the docs confirmed this, which reminded me to always RTFM!

Updating my wire method handler to capture the whole thing rather than the records from the server:

@wire(GetPage, {name: 'home'})
gotPage(result) {
    if (result.data) {
        this.page=result.data;
        this.dataToRefresh=result;
    }
and things were all good:


Notice that I don't have to care what data was returned by the wired method, I just save it somewhere and pass it as the parameter.

Example Component

My LWC Blogs repo has an example component that shows refreshApex being used correctly. If you are going to try it out in a scratch org, check the instructions in the README as there's a little bit of setup to get the page to work correctly. 

The component retrieves a Page record that has the name 'Home' and shows how many views it has had. There's a button you can click that increments the views count by calling an Apex method. Once the method completes, the record cached server side is updated using the refreshApex method, and the user receives notifications about all sorts of things:



Related Posts


Sunday, 9 January 2022

ApexTypeImplementor in Spring 22



Note: This feature may be in Beta come Spring 22 - the release notes in Salesforce Help say that it is in beta, the PDF version doesn't mention it. If I get confirmation either way I'll update this post! Remember that Spring 22 is still in preview, so this feature might never see the light of day anyway!

Introduction

The Spring 22 release of Salesforce introduces the ApexTypeImplementor object, which contains information about the classes that implement an interface, either directly or indirectly. You've been able to access Apex class bodies for years, so picking up the direct implementation of an interface has been possible for a while. Figuring out that that the interface is implemented in the inheritance hierarchy gets a bit more tricky. In either case, having the platform take care of it for you takes away a chunk of code, which is always a good thing.

Use Cases

When I first came across this new feature it had a touch of the "solution looking for a problem" about it, but once I had a chance to dig into it in a little more detail, I have a number of scenarios where I'll be able to apply it as soon as it is available/maybe out of beta.

Rule Engine

The most common use case from my perspective is the configurable plug and play engine. I have several rules engines where a class is considered a rule because it implements a specific interface. I'll have a number of classes that implement rule variants, and these rules are enabled and applied in a specific order based on configuration. 

The ApexTypeImplementor doesn't help in terms of which rule classes should be applied, as I still want it to be driven through configuration rather than the code just applying everything it can find in whatever order they come back. Where it does help is to assist the admin who is configuring the rules. Rather than them having to remember the names of classes or look them up and copy/paste from other setup pages, I can create a custom configuration page that offers them only the classes that implement the interface, thus ensuring that the configured classes are available.

Setup Verification 

A less obvious use case is confirmation that a system is setup and configured correctly - i.e. that there is no missing or obviously broken configuration that will cause errors at runtime. Each feature that can be enabled has an associated class that checks the feature is configured correctly (or at least plausibly as it's quite hard to confirm correctness without running a bunch of tests). As long as each of these classes implements the same interface, I can execute code from an admin clicking a button or quick action that finds all of the classes that implement the verification interface. Each of these is instantiated and the method that checks the configuration is executed. This works really well if the additional features are delivered via org dependent or unlocked packages, as the code that implements the feature and the code that checks it are developed in tandem.

Training Confirmation

The third use case that I have is around training, and ensuring that the training has been retained. I have a collection of packages that I can drop into an org that ask the user to carry out some configuration or regular business work, and then check that they carried it out correctly. 

At the moment the packages create configuration entries when they are installed, and those configuration entries are used to generate cards on an application page so that users can access and check the challenges. Because I want all of the challenges that are installed to be available to the user, with the ApexTypeImplementor I can do away with the configuration entries and just show the users details of all the classes that implement the interface. 

Sample App

I've created a sample application in my Spring 22 repository to show ApexTypeImplementor used in anger. I have an interface that a class implements to describe itself to interested parties :

public interface ClassInfoIF 
{
    String getDescription();
    String getAuthor();
}

I'm using a Lightning Web Component to display the details of the classes, which extracts them from a dedicated controller (that also implements the interface!) : 

@AuraEnabled(cacheable=true)
public static List<ClassDetail> GetClassDetails()
{
    List<ClassDetail> classDetails=new List<ClassDetail>();
    List<ApexTypeImplementor> classInfoTypes = 
           [SELECT ApexClassId, ClassName, ClassNamespacePrefix
            FROM ApexTypeImplementor
            WHERE InterfaceName = 'ClassInfoIF' and IsConcrete=true];

    for (ApexTypeImplementor classInfoType : classInfoTypes)
    {
        ClassDetail classDetail=new ClassDetail();
        classDetail.classId=classInfoType.ApexClassId;
        classDetail.fullName='';
        if (null!=classInfoType.ClassNamespacePrefix)
        {
            classDetail.fullName=classInfoType.ClassNamespacePrefix + '.';
        }

        classDetail.fullName+=classInfoType.ClassName;
        ClassInfoIF classInfo = 
                    (ClassInfoIF) Type.forName(classInfoType.ClassNamespacePrefix,
                                            classInfoType.ClassName).newInstance();

        classDetail.description=classInfo.getDescription();
        classDetail.author=classInfo.getAuthor();
        classDetails.add(classDetail);
    }

    return classDetails;
}

The bolded sections above show the query that extracts the classes that implement the interface, and the code that constructs the class as an instance of ClassInfoIF and executes the methods that describe the class. If a new class is added to the org that implements this interface, it is automatically included the next time I access the page.

I've put in a few fake classes too, including one that implements the interface by overriding methods from its superclass to check the indirect side of things works too. Accessing the application page shows the information I'm interested in for each class:


Related

Tuesday, 4 January 2022

2021 Year in Review - Part 4


Testing. Testing. 1..2..3 (and more) Lateral Flows

The theme of the last few months of the year was testing. Not the fun kind involving mocks and setup/teardown, but the unpleasant kind that required shoving swabs up your nose. The picture everyone wanted to post on social media had changed from a selfie to a negative result!

It was all worth it though.

October

The London Salesforce Developers were back in person! This felt simultaneously wonderful and very weird - seeing three dimensional versions of faces I'd only seen on screens for 18 months took a little bit of getting used to. I also got the opportunity to meet in person a bunch of graduates that I'd been training up for the last few months!

The theme of the meetup was a quick run through some of the interesting features from the Winter 22 release that had gone live earlier in the month. We kept it pretty light though, as most people just wanted to network. 

Bret Taylor was apparently telling everyone he was going to be promoted to Salesforce CEO soon. He wasn't wrong, but as it turned out wasn't right either.

November

Back to back in-person events for the London Salesforce Developers, as Rob Cowell presented a cracker of a session on Integrating AWS with Salesforce. There was also a dog, which made it pretty much perfect for me.


And the biggest news of the month, for me at least, BrightGen was majority acquired by Credera.

December

Just ahead of the Omicron variant, the Xmas Megameet of the London Salesforce Developers, Admins and Women in Tech took place on 6th December.  

After months of rumours it finally happened, although not exactly as some were expecting, Bret Taylor was promoted to co-CEO of Salesforce. He was also took over as Chairman of the Board of Twitter. Not a bad week's work by any measure.

Dreamforce made it outside of San Francisco, although sadly for those of us in Europe it was only to the other side of the US in New York. The safety aspects of this were taken extremely seriously, and held up as an example for everyone else. And launched as a product - Dreampass. What with the reimagined work.com in 2020, the acquisition of Slack and now Dreampass, Salesforce have certainly maximised opportunities during these pandemic times.

What Does 2022 Hold?

More Covid cases for sure - the numbers continue to rise rapidly in a number of locations including the UK. Work from home is again the advice, so I'd expect the first couple of London Salesforce Developer meetings to be virtual again. Hopefully three months of in-person softens that particular blow!

I can't see Salesforce resting on their laurels with regard to remote work, so I'd expect more in the way of acquisitions to establish themselves as a player in this space. Videoconferencing still seems like an obvious addition to the product set, and it will be interesting to see if (and how) Slack is further integrated  into the Salesforce UI, given that it will supposedly replace Chatter.  Half of employees say they may quit if remote work goes away, so there's plenty more opportunity in this space I feel.

Two things are for sure, there will be three further releases of Salesforce in 2022, and if there is a major outage it will be in May!

Whatever happens, I'm sure I'll be banging on about it on here or Substack. Thanks for reading!

Related Posts


Monday, 3 January 2022

2021 Year in Review - Part 3

July

Another month, another new (and virtual) event. The first Consultancy Dreamin' took place on the Hopin platform. In a change of pace for me, I was looking after a cohort of speakers rather than running any sessions myself. I was also (and somewhat unexpectedly, as I'd missed the training session) facilitating a couple of panel sessions, which was great fun. Usually at these events I just have to worry about getting myself somewhere at the right time and not messing up my demo - it's a very different experience organising others, especially once the EMEA and US aspects of the event started to overlap!

July also saw my personal favourite event for the London Salesforce Developers - Discover a lightning fast way to debug in Salesforce with RFLIB. I've long been convinced that platform events are a great way to decouple automation from database transactions, and this is an excellent use of them.

The Salesforce acquisition of Slack finally went through, although it felt like it had already happened some time before. Almost simultaneously, a bunch of Trailhead badges for Slack went live and the hunt to get them all started up again. It wasn't all unicorns and rainbows though, as the Times New Express reminded us of the Salesforce execs that had departed to this point in 2021.

August

Salesforce launched Salesforce+ - the first (and only?) enterprise software company streaming service. While I've done some gentle mocking of this (here and here), I do think it's a great idea. It was also good to see Salesforce trying something different on the event front, albeit still trapped in front of a screen!

I reviewed Ahsan Zafar's book on Salesforce Data Architecture and Management - a good book tackling a tricky subject. Remote working was also something I was spending a lot of time thinking about, with particular reference to returning to something like normality.

An old friend, and former leader, of the London Salesforce Developers - Anup Jadhav - returned to tell us all about OmniStudio. This was an extremely well attended session, second only to Erika McEvilly's trigger session that kicked the year off. 

September

Dreamforce was back. Sort of. The events around the world had disappeared and San Francisco was now 1,000 attendees over 2 days, but it did happen and people were there. It was streamed on Salesforce+ rather than trying to recreate the physical event on a virtual platform, which made a bit of a change, but still kept us in front of our screens. The London Salesforce Developers had another virtual virtual watch party, but we were seeing the numbers drop as people were becoming zoomed out.  This chart of our attendance figures shows the decline - it was slow, but definitely heading down aside from the occasional event.

for this reason we'd decided to go back in-person in October. Exciting times!

Related Posts 

Sunday, 2 January 2022

2021 Year in Review - Part 2


The lockdown locks before and after!

April

Lockdown finally started to ease in the UK, with non-essential retail starting to open up and overnight stays away from home allowed again. Outdoor mixing was allowed, but with a maximum of six people from two households, so we didn't feel it made a lot of sense to move the London Salesforce Developers back in person just yet.  My fellow co-organiser, Amnon Kruvi, gave a well attended virtual talk on the Salesforce Security Review. Judging by the questions, there are clearly a lot of future ISVs among our members.

The rumour mill suggested that Salesforce were committed to an in person Dreamforce in 2021, which turned out to be accurate, although calling what happened Dreamforce seems a bit of a stretch when only a small number of people from the US were allowed to attend. I guess it was important to be seen to be holding it, but it's hard to agree with "Dreamforce is an annual event that brings together the global Salesforce community" when the vast majority of said community have to watch on a streaming platform.

Summer 21 Pre-release signups opened up, reminding us that we are never more than a few months away from a Salesforce release.

I also reviewed Tameem Bahri's book: Becoming a Salesforce Architect - I liked it then and I still like it now!

May

While you might never be more than a few months away from a Salesforce release, when it's May you also appear to be at the most risk of an outage, and 2021 continued the trend with a DNS issue. While the issue took the trust site down, it was good to see Salesforce continue to send people there to maximise their frustration. Why limit yourself to unhappy customers when you can turn them incandescent with rage?

Paul Battisson joined the London Salesforce Developers to tell us how to improve the performance of our Apex code by turning it up to 11.

Continuing with the performance theme, this month also saw the publication of one of my most popular blogs of the year - The Impact of System.Debug, which garnered close to 3,500 views, several comments and a new Apex PMD rule!

June

After 15 months without letting any scissors near me, the lockdown locks finally went, freeing me up from several minutes of grooming every week and raising over £500 for charity. They'd provided a lot of pleasure for a lot of people, mostly in the form of pointing and laughing, but their day was done.

The Summer 21 release hit production, and Salesforce published the root cause analysis of this year's instalment of the May outage. There was a new Event Bus on the way, with talk of a new pub/sub API. Six months later there's a few references to a pilot and a guide, but the marketing seems to have been dialled right down. Shades of Evergreen/Functions maybe?

Although countries were starting to open up, the virtual events kept coming, with the trailheadx developer conference. Scheduled with an eye on San Francisco local time, the near to 5pm start wasn't ideal for those of us in the UK, so I mostly caught up on the content over the next couple of months. Mostly, because the London Salesforce Developers ran a viewing party for the keynote, where we all joined a video call to watch a keynote on video. Very meta. To ensure everyone paid attention we had a word hunt, where spotting common terms from Salesforce keynotes (awesome, 1-1-1, you know the kind of thing) earned swag. An unexpected side effect of this was the entertainment of people joining an hour into the session and hoping they were the first to hear 'awesome'. Entertaining for me, as I wasn't running the competition!

Dreamforce was announced as a series of in-person events around the world, although everything outside of San Francisco quietly disappeared over the next few months. 

Related Posts

2021 Year in Review - Part 1