Pages

Monday, May 18, 2020

Part I : Understanding Change Data Capture using Asynchronous Apex Triggers and Handling Platform Event in Lightning Web Components

Salesforce introduced Change Data Capture and Asynchronous Apex Triggers in Summer 19 release. Change Data Capture publishes change events, which represents changes to Salesforce records. Changes include the creation of a new record, updates to an existing record, deletion of a record, and undeletion of a record.

Available in: both Salesforce Classic and Lightning Experience
Available in: Enterprise, Performance, Unlimited, and Developer editions


Use of Change Data Capture events to:
  • Receive notifications of Salesforce record changes, including create, update, delete, and undelete operations.
  • Capture field changes for all records.
  • Get broad access to all data regardless of sharing rules.
  • Get information about the change in the event header, such as the origin of the change, which allows ignoring changes that your client generates.
  • Perform data updates using transaction boundaries.
  • Use a versioned event schema.
  • Subscribe to mass changes in a scalable way.
  • Get access to retained events for up to three days
Supported Objects

Change events are available for all custom objects defined in your Salesforce org and a subset of standard objects. To see the complete list of supported standard objects, please click here.


Understanding the Flow for Change Data Capture

So when a record is created or updated, Change Data Capture publishes an event and a change event trigger can then process that event asynchronously. Above is a simple example to understand the flow better. Once any record completes its execution then change data publish an event and then Asynchronous Apex Trigger starts processing.


Setup Change Data Capture

First, we need to enable Change Data Capture for the object. In Setup Search for Change data Capture and open it.



Once we have enabled it, we need to subscribe to the change events. To subscribe to the change events we will be using Subscribe with Apex Triggers. But before diving to apex triggers we need to understand the naming conventions for the channels which will be subscribing to the change.


Subscription Channels

Use the subscription channel that corresponds to the change notifications you want to receive. The channel name is case-sensitive.

All Change Events
/data/ChangeEvents

A Standard Object
/data/(Standar Object Name)ChangeEvent

For example, the channel to subscribe to change events for Case records is:
/data/CaseChangeEvent

A Custom Object
/data/(Custom Object Name)__ChangeEvent

For example, the channel to subscribe to change events for Candidate__c custom object records is:
/data/Candidate__ChangeEvent

If you want to learn more about Change Data Capture, please click here.


Subscription Using Apex Triggers

The change event trigger fires when one or a batch of change events is received. The change event trigger is not like object triggers, it runs asynchronously after the database transaction is completed. The asynchronous execution makes change event triggers ideal for processing resource-intensive business logic while keeping transaction-based logic in the object trigger. Change event triggers can help reduce transaction processing time.

Below is the example:




trigger CaseChangeEventTrigger on CaseChangeEvent (after insert) {

    List<CaseChangeEvent> changes = Trigger.new;
    
    Set<String> caseIds = new Set<String>();
    
    for (CaseChangeEvent change : changes) {
        // Get all Record Ids for this change and add to the set
        List<String> recordIds = change.ChangeEventHeader.getRecordIds();
        caseIds.addAll(recordIds);
    }
    
    // Publish platform events for predicted red accounts
    List<DemoLight12__Case_Change_Event__e> caseChangeEvents = new List<DemoLight12__Case_Change_Event__e>();
    for (Case caseObj : [Select Id,CaseNumber from Case where Id in: caseIds]) {
        caseChangeEvents.add(new DemoLight12__Case_Change_Event__e(DemoLight12__CaseNumber__c=caseObj.CaseNumber));
    }
    
    System.debug('RED_ACCT: ' + caseChangeEvents);
    if (caseChangeEvents.size() > 0) {
        EventBus.publish(caseChangeEvents);
    }
}

In the above code, I have created a platform event as Case_Change_Event__e, and by using EventBus.publish method I have published an event message.

If you want to learn more about Platform Events, please click here.


Handling Platform Events in Lightning Components

The lightning/empApi module provides access to methods for subscribing to a streaming channel and listening to event messages. All streaming channels are supported, including channels for platform events, PushTopic events, generic events, and Change Data Capture events.

Note:
  • This component is available in the 44.0 API version or later.
  • This is available in Lightning Experience and only supported in desktop browsers.
Use case: I want to display a message on the Case Detail record page that particular case has been updated by others and the logged-in person is working on different cases, so on the case detail page logged in person will get a message displayed at the top which case has been updated. Below are the components I have used to achieve my use case:

  • Setup Change Data Capture on Case object explained above.
  • Create a Platform Event as Case_Change_Event__e and one custom field as Casenumber__c within the platform event object to store the CaseNumber which got updated.
  • Create an Async Apex trigger as CaseChangeEventTrigger (explained above) to publish the platform event.
  • Custom LWC component as caseChangeEvent which will subscribe to the platform event and will update the message on UI.


Below is the code for the LWC component which will be added on the Case Detail record page and will subscribe to the platform event and display the message of any records got updated.


caseChangeEvent.html


<template>
    <lightning-card title="Pinned Updates on Cases" icon-name="custom:custom14">
        <div class="slds-m-around_medium">
            {finalMessage}
        </div>
        
    </lightning-card>
</template>


caseChangeEvent.js



import { LightningElement, api } from 'lwc';
import { subscribe, unsubscribe, onError, setDebugFlag, isEmpEnabled } from 'lightning/empApi';

export default class EmpApiLWC extends LightningElement {
    channelName = '/event/DemoLight12__Case_Change_Event__e';
    isSubscribeDisabled = false;
    isUnsubscribeDisabled = !this.isSubscribeDisabled;

    message;

    renderedCallback() {
        const messageCallback = (response) => {
            console.log('New message received : ', JSON.stringify(response));
            // Response contains the payload of the new message received
            console.log('New message received : ', response.data.payload.DemoLight12__CaseNumber__c);
            this.message = 'Case ' + response.data.payload.DemoLight12__CaseNumber__c + ' has been updated';
        };

        // Invoke subscribe method of empApi. Pass reference to messageCallback
        subscribe(this.channelName, -1, messageCallback).then(response => {
            // Response contains the subscription information on successful subscribe call
            console.log('response === ' + JSON.stringify(response));
            console.log('Successfully subscribed to : ', JSON.stringify(response.channel));
        })
    }

    registerErrorListener() {
        // Invoke onError empApi method
        onError(error => {
            console.log('Received error from server: ', JSON.stringify(error));
            // Error contains the server-side error
        });
    }

    get finalMessage(){
        return this.message;
    }
}

If you observe in the above code I have imported lightning/empApi which provides below method

  • subscribe - Subscribes to a given channel
  • unsubscribe - Unsubscribes from the channel using the given subscription object
  • onError - Registers a listener to errors that the server returns.
  • setDebugFlag - Set to true or false to turn console logging on or off respectively.
  • isEmpEnabled - Returns a promise that holds a Boolean value. The value is true if the EmpJs Streaming API library can be used in this context; otherwise false.
To learn more about lightning/empApi, please click here.

caseChangeEvent.js-meta.xml


<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>48.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
       <target>lightning__RecordPage</target>
   </targets>
</LightningComponentBundle>

Live Demo:



I hope this will help to understand platform events handling in lightning components. 

Looking forward to everyone's suggestions and comments!!!