Enable/Disable Process Builder Processes all at once

I wrote a lightning component to allow me to easily enable and disable flows en masse.

It lists all the latest Flows, and give you buttons to:

  • Toggle Selected Flows
  • Enable All Flows
  • Disable All Flows

Component

<aura:component controller="FlowManagerController" implements="flexipage:availableForAllPageTypes">

<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

<aura:attribute name="flows" type="Object"/>
<aura:attribute name="selectedFlows" type="List"/>
<aura:attribute name="columns" type="List" default="[                                        
                                                    {label: 'Developer Name', fieldName: 'DeveloperName', initialWidth: 500, type: 'text' },                                             
                                                    {label: 'Active', fieldName: 'Active', initialWidth: 150, type: 'boolean'},                                             
                                                    {label: 'Action', type: 'button', initialWidth: 200, typeAttributes:
                                                    {label: 'Change Status', name: 'change_status' }},
                                                    {label: 'Latest Version', fieldName: 'LatestVersionNumber', initialWidth: 150, type: 'number' },                                             
                                                    {label: 'Active Version', fieldName: 'ActiveVersionNumber', initialWidth: 150, type: 'number' },                                             
                                                    ]" />


<aura:attribute name="loading" type="Boolean" default="false"/>

<aura:if isTrue="{!v.loading}">
    <lightning:spinner alternativeText="Loading" size="large" />
</aura:if>

<div class="slds-card slds-p-around_small">

    <div class="slds-p-bottom_small">
        <lightning:button 
                          label="Toggle selected Flows"
                          variant="brand"
                          onclick="{! c.handleToggleSelectedFlows }"
                          />

        <lightning:button 
                          label="Enable All Flows"
                          variant="brand"
                          onclick="{! c.handleEnableAllFlows }"
                          />

        <lightning:button 
                          label="Disable All Flows"
                          variant="brand"
                          onclick="{! c.handleDisableAllFlows }"
                          />

    </div>

    <lightning:datatable keyField="id"
                         data="{! v.flows }"
                         onrowaction="{! c.handleRowAction }"
                         columns="{! v.columns }"
                         onrowselection="{! c.handleRowSelection }"
                         showRowNumberColumn="true" />

</div>

</aura:component>

JavaScript Controller

({
    doInit : function(cmp, event, helper) {
        helper.getFlows(cmp);
    },
    handleToggleSelectedFlows : function(cmp, event, helper){
        helper.showLoading(cmp);
        let flows = cmp.get("v.selectedFlows");
        console.log(flows);
        if (flows && flows.length > 0){
            let promises = [];
            flows.forEach(function(row) {
                promises.push(helper.updateFlowPromise(cmp, row));
            });
            Promise.all(promises).then(function(results){
                console.log(results); 
                helper.getFlows(cmp);
                helper.hideLoading(cmp);
            });
        }
    },
    handleEnableAllFlows : function(cmp, event, helper){
        helper.showLoading(cmp);
        let flows = cmp.get("v.flows");
        if (flows && flows.length > 0){
            let promises = [];
            flows.forEach(function(row){
                if (row.Active == false) promises.push(helper.updateFlowPromise(cmp, row));          
            });
            Promise.all(promises).then(function(results){
                console.log(results); 
                helper.getFlows(cmp);
                helper.hideLoading(cmp);
            });
        }
    },
    handleDisableAllFlows : function(cmp, event, helper){
        helper.showLoading(cmp);
        let flows = cmp.get("v.flows");
        if (flows && flows.length > 0){
            let promises = [];
            flows.forEach(function(row) {
                if (row.Active) promises.push(helper.updateFlowPromise(cmp, row));              
            });
            Promise.all(promises).then(function(results){
                console.log(results); 
                helper.getFlows(cmp);
                helper.hideLoading(cmp);
            });
        }
    },
    handleRowSelection : function(cmp, event, helper){
        let flows = event.getParam('selectedRows');
        console.log(flows);
        cmp.set("v.selectedFlows", flows);
    },
    handleRowAction: function(cmp, event, helper) {
        var action = event.getParam('action');               
        if (action.name =='change_status') {
            var row = event.getParam('row');        
            console.log(row);   
            helper.updateFlow(cmp, row);
        }
    }
})

JavaScript Helper

({
    showLoading : function(cmp){
        cmp.set("v.loading", true);  
    },
    hideLoading : function(cmp){
        cmp.set("v.loading", false);  
    },
    getFlows : function(cmp) {
        this.callAction(cmp, 'c.getFlows', null, function(results) {
            if (results == null) return;
            let data = JSON.parse(results);
            let records = data.records;
            if (records && records.length > 0) {
                records.forEach(function(r){
                    if (r.ActiveVersion){
                        r.ActiveVersionNumber = r.ActiveVersion.VersionNumber;     
                        r.Active = true;
                    } else {
                        r.ActiveVersionNumber = null;
                        r.Active = false;
                    }
                    r.LatestVersionNumber = r.LatestVersion.VersionNumber;
                }); 
            }
            cmp.set("v.flows", records);
        });  
    },
    updateFlow : function(cmp, row){
        let self = this;
        let params = this.getParams(row);
        this.callAction(cmp, 'c.updateFlow', params, function(result){
            console.log(result);
            if (result == true) self.getFlows(cmp);
        })  
    },
    updateFlowPromise : function(cmp, row){
        let params = this.getParams(row);
        return this.promiseAction(cmp, 'c.updateFlow', params);
    },
    getParams : function(row){
        let flowId = row.Id;
        let versionNumber = row.LatestVersionNumber;
        if (row.Active) versionNumber = null;        
        let metadata = {
            'Metadata': {
                'activeVersionNumber': versionNumber
            }
        }
        let params = {};
        params.flowId = flowId;
        params.metadata = JSON.stringify(metadata);
        return params;
    },
    promiseAction : function(cmp, methodName, params){
        console.log(methodName);
        console.log(params);
        var self = this;
        return new Promise(function(resolve, reject) {     
            var action = cmp.get(methodName); 
            if (params != null) action.setParams(params);   
            action.setCallback(this, function(response) {
                var state = response.getState();
                console.log(methodName + ' ' + state);
                if(cmp.isValid() && state === "SUCCESS"){
                    var result = response.getReturnValue();
                    //console.log(result);
                    resolve(result);
                } else if (state === "ERROR"){
                    var errors = response.getError();
                    self.handleErrors(errors);
                    reject(errors);
                }
            });
            $A.getCallback(function() {
                $A.enqueueAction(action); 
            })(); 
        }); 
    },
    callAction : function(cmp, methodName, params, callback){
        var self = this;
        var action = cmp.get(methodName); 
        if (params != null) action.setParams(params);
        action.setCallback(this, function(response) {
            var state = response.getState();
            console.log(methodName + ' ' + state);
            if(cmp.isValid() && state === "SUCCESS"){
                var result = response.getReturnValue();
                //console.log(result);
                if (callback) callback(result);
            } else if (state === "ERROR"){
                self.handleErrors(response.getError());
            }
        });

        $A.getCallback(function() {
            $A.enqueueAction(action); 
        })();  
    },
    handleErrors : function(errors) {
        // Configure error toast
        let toastParams = {
            title: "Error",
            message: "Unknown error", // Default error message
            type: "error"
        };
        // use the error message if any
        if (errors) {
            if (errors[0] && errors[0].message) {
                console.log(errors[0].message);
                toastParams.message = errors[0].message;
            }
        }
        // Fire error toast
        let toastEvent = $A.get("e.force:showToast");
        toastEvent.setParams(toastParams);
        toastEvent.fire();
    },
})

Apex Controller

public class FlowManagerController {

    private static final string RESOURCE_TOOLING_QUERY_FLOW = '/tooling/query?q=Select+Id,ActiveVersion.VersionNumber,LatestVersion.VersionNumber,DeveloperName+From+FlowDefinition+ORDER+BY+DeveloperName';
    private static final string RESOURCE_TOOLING_SOBJECT_FLOW = '/tooling/sobjects/FlowDefinition/';

    @AuraEnabled
    public static String getFlows() {

        HttpResponse response = executeCallout('GET', RESOURCE_TOOLING_QUERY_FLOW, null);

        System.debug(response);

        if (response.getStatusCode() == 200) {
            return response.getBody();
        }

        throw new AuraHandledException(response.toString());
    }

    @AuraEnabled
    public static boolean updateFlow(String flowId, String metadata) {

        HttpResponse response = executeCallout('PATCH', RESOURCE_TOOLING_SOBJECT_FLOW + flowId + '/?_HttpMethod=PATCH', metadata);

        System.debug(response);

        if (response.getStatusCode() == 200 || response.getStatusCode() == 204) {
            return true;
        }

        return false;
    }

    public static HttpResponse executeCallout(String method, String resourceName, String requestBody) {

        HttpRequest request = new HttpRequest();

        if (method == 'PATCH') {
            request.setMethod('POST');    
        } else {
            request.setMethod(method);    
        }

        request.setEndpoint(getUrl() + resourceName);

        request.setHeader('Authorization', 'OAuth {!$Credential.OAuthToken}');

        request.setHeader('Accept', 'application/json');
        request.setHeader('X-PrettyPrint', '1');
        request.setHeader('Content-Type', 'application/json');

        if (String.isNotBlank(requestBody)) {
            request.setBody(requestBody);
        }

        System.debug(request);

        return new Http().send(request);
    }

    private static String getUrl(){
        return 'callout:Named_Credential'; // CHANGE THIS
    }
}

SETUP

  1. Create Connected App
  2. Create Auth. Provider
  3. Create Named Credential
  4. Create Lightning App Page called Flow Manager