Learn MOAR in Spring ’20 with Lightning Flow Features

Discover Spring ’20 Release features! We are sharing release highlights for Developers (and Admins), curated and published by Salesforce product experts, as part of Learn MOAR. Complete the trailmix by March 31, 2020 to receive a special community badge and unlock a $10 contribution to FIRST®.

With Spring ’20 we continue to invest in features that enable you to design & implement against requirements with an eye toward flexibility and reusability. Today we cover running Flows in system context, running Flows on record changes before the save, and writing invocable actions that can accept generic sObject types.

Run Flows in System Context (with sharing)

With Spring ‘20, we can now set flows to run in system context (with sharing). When a user runs a flow that is in system context (with sharing), object- and field-level security checks are automatically passed when the Flow executes a CRUD element. This is a great way to escalate a user’s privileges during specific business processes without having to manually assign that user the necessary CRUD & FLS permissions for every object in that process. This feature is best leveraged by taking advantage of the previously released Winter ’20 feature that gives you the ability to restrict access to run flows via profiles & permission sets. We also give you additional finer-grained control of privilege escalation with subflows, in case you want some steps of a business process to run in user context and other steps to run in system context.

As an example, suppose you want to create a guided Flow for agents to follow when handling customer support cases. At the end of this Flow, you want the case status to be updated according to the actions that were taken during the Flow. However, while you’re okay with agents directly looking at the case record, you don’t want them manually updating the case status — thus, you set up FLS so that they can read—but not edit—case status.

By setting the Flow to run in system context (with sharing), you can put an Edit element at the end of the Flow and have it update the cases status even when the running user doesn’t have edit access to that field. If you want only this element to run in system context, you can put it into its own system-level Flow, then invoke that Flow as a subflow rather than setting your entire top-level case management Flow to run in system context.

To handle the volume of updates to flows, here are some rules governing how Flows run, what context they run in, and how sharing rules are enforced

  1. Process Builder processes run in system context, without sharing.
  2. Scheduled Flows run in system context, without sharing. Additionally, scheduled flows are run as the Automated Process user.
  3. Top-level, auto-launched Flows and screen Flows run in user context.
  4. Flows that are not entered at the top-level — that is, Flows that are invoked by other processes such as Apex triggers, process builder processes, or even other Flows — run in the execution context of their caller.
  5. [New] Declaratively, you can explicitly declare an auto-launched or screen Flow to always run in system context, with sharing. When this option is selected, the Flow will always run in system context, with sharing, whether it’s entered at the top-level or it’s invoked from another place.

Run Flows on Record Changes (before the save)

If you check out the Triggers and Order of Execution page in the Apex Developer Guide, you’ll notice a new entry: “3. Executes flows that make before-save updates.” That’s right, we’ve added before-save Flow triggers!

Since these Flows run before the save point, they don’t actually cause DML. This helps us in a few different ways:

  1. We avoid recursive, or cascading, saves.
  2. We don’t accrue toward the Apex Governor’s DML limit.
  3. The operations are much more performant: up to 10x faster (or more) than the functionally equivalent Process Builder process and (gasp) Workflow Rule.

In this release, you’re only allowed to make updates to fields on the underlying record. Just like before-save Apex triggers, you don’t need an explicit CRUD operation to update the record: there’s an implicit save at the end of the process.

If you’re looking for a declarative way to automatically set field values when a record is inserted or updated, this is the tool for you. Before-save Flow triggers run in system context, without sharing.

One Invocable Action to Rule Them All

Invocable actions now accept the generic sObject data type! This means you can now create a single Apex action that can be reused across multiple Flow use cases involving distinct sObject records and record collections.

If you’ve got a bunch of invocable actions sitting around that are functionally equivalent, except that they operate on different sObjects, you’ve now got an opportunity to clean that code up & refactor to a single invocable action. And as you author more Apex invocable actions, consider if you can design them to be more generic & adaptable to other use cases.

To show you how it’s done, we’ve created a screen flow that create and upserts different object data leveraging a generic invocable action in Apex.

Our data entry screen keeps things simple with 3 fields for a user to input data.

Then we map the data to the correct variables in flow, and pass that information to an Apex Action. Our first Apex Action upserts an Account. It does this by calling our “Upsert Records” Apex action.Screen Shot 2020-01-23 at 1.22.50 PM.png
With the data set in the flow, we can then use our upsertRecords class to upsert the data to the specific object.

public class upsertRecords {
    @InvocableMethod(
        label = 'Upsert Records'
        description = 'Update some records, or if they don' exist, create them.')
    public static List<UpsertResult> upsertRecords(List<UpsertRequest> request) {
        List<SObject> records = new List<SObject>();
        for (UpsertRequest req: request) {
            records.addAll(req.records);
        }
        List<UpsertResult> ls = new List<UpsertResult> ();
        UpsertResult res = new UpsertResult();
        //get sobject type
        Schema.SObjectType sObjectType = records.get(0).getSObjectType();
        if (sObjectType != null) {
            String listType = 'List<' + sObjectType + '>';
            List<SObject> castRecords = (List<SObject> ) Type.forName(listType).newInstance();
            castRecords.addAll(records);
            upsert castRecords;
            res.sOuts = castRecords;
            ls.add(res);
        }
        return ls;
    }
    public class UpsertRequest {
        @InvocableVariable(label = 'Records to Upsert')
        public List<SObject> records;
    }
    public class UpsertResult {
        @InvocableVariable(label = 'Upserted Records')
        public List<SObject> sOuts;
    }
}

Notice that the Apex code is written completely generically and agnostic to the input’s sObject type: the Apex developer simply authors a single invocable action, and then the Flow admin gets to select which sObject(s) they want to use it for. Later in our example, we upsert to Contact as well, but it could be any other sObject.

Reducing code surface area of maintenance & increasing reusability? That’s a win-win!

Conclusion

The three features we discussed today are just a few of the many features that the Flow team has delivered in service of helping you and your team be more successful. We’re deeply grateful for the support and feedback that you’ve provided us across every channel, and remain committed to making it ever easier to develop and maintain process-driven applications here on platform.

About the author

Tim Peng is the PM for the Flow engine. When he’s not fussing about data models & APIs, you can find him in the snack section at Trader Joe’s or obsessively researching his next travel destination.