Lightning · Salesforce

Salesforce Summer’17 Release: Platform Events

Platform events is a native feature introduced in summer’17 release. It follows an event driven architecture wherein a sender broadcasts a message that is captured by one or more receivers. Platform events simplify the entire process of communicating changes and responding to them without the need of complex logic. Platform events allow customers to increase interaction between data and Salesforce. There is no need to poll the server to obtain information about a state. Instead you subscribe to an event channel that will notify in case of a change in state.

To create a Platform Event

You can access Platform events through Setup.
Setup->Build->Develop->Platform Events.

1

Considerations in Platform Events

  1.  Platform Events are similar to Custom Objects with certain limitations. The API name of Platform Events ends with __e.
  2.  Platform events unlike custom objects are immutable that means they cannot be updated or deleted once they are published. Platform events cannot be queried using SOQL or SOSL.
  3.  Events can be created using Process Builder just like any other sObject.
  4.  Event notifications are instances of Platform Events which cannot be updated unlike sObjects.
  5.  These cannot be viewed in the user interface. Platform Event definitions once deleted cannot be restored. In that case make sure you delete any associated triggers. Before you rename an event ensure that you delete all the associated triggers.
  6.  There are no tabs associated with Platform Events as records cannot be viewed in salesforce interface.
  7.  Lightning App Builder the object definition is visible but you cannot create a lightning record page for platform events as the records are not visible in user interface.
  8.  Upto 10,000 platform events can be published per hour.
  9.  Custom Fields in Platform Events can be only of type Text, Text Area(Long), Checkbox, Number, Date, Date/Time.
  10.  Only events that were sent in the past 24 hours can be replayed. They can be replayed using only API and not Apex.
  11.  You can set read and create permissions on Platform events.
  12.  Visualforce and Lightning components can subscribe to Platform events using CometD.
  13.  Platform events are like generic events but with more powerful customization. You can publish custom data.
  14.  Once the Platform Events are defined, event notifications can be published and events can be subscribed using Apex or API.

10.PNG

Platform Event Definition Details

11.PNG

Standard Fields in Platform Events

This is the standard fields included while creating a Platform Events. You can also include your own custom fields.

12

Components of Platform Events

Platform Events include five main components. Event messages are notifications that your app sends or receives to take actions. The pipe is like a message bus that allows events to be added in chronological order along with their Replay ID. The window will slide with time and any event within the window can be replayed. Publishers are apps that push events into the pipe. These apps can be internal or external. Internal events can be published using flows or Process Builders or can be used programmatically with Apex. External apps use SOAP, REST or similar native API’s to publish events. Consumers can subscribe to events using CometD/Bayeux and with Apex. Support with Flows and Process Builders with be included in the future release.

Create a new Platform Event

You can create a platform event that will publish an event when a new vacancy is created.

2

Just like Custom objects, custom fields can be created for Platform events. We have two custom fields for position number and type that is generated.

3.PNG

You can publish event notifications and subscribe to events using apex or an API. In Apex Event.publish() method is used. To receive these events an after insert trigger is used on the event object. Salesforce creates a channel for each defined platform event.

You can publish events using API by inserting records in your event object just like any other sObject. Any salesforce API such as SOAP, REST or Bulk API can be used to publish events. There is no extra call required to publish events. You can subscribe to events using CometD or writing an after insert triggers on your event object.

Publish Platform Event Notifications

Here we will create two events of type vacancy. We will require Vacancy (or any other that depends on your use case) Platform event to be defined in your Salesforce instance.

public class NewVacancyClass {
    public static void createnewvacancy(){
    List<Vacancy__e> vacantEvents = new List<Vacancy__e>();
    vacantEvents.add(new Vacancy__e(Position_Number__c='0105', Position_Type__c='Developer'));
    vacantEvents.add(new Vacancy__e(Position_Number__c='0106', Position_Type__c='Senior Developer'));

// Call method to publish events
	List<Database.SaveResult> results = EventBus.publish(vacantEvents);

// Inspect publishing result for each event
   for (Database.SaveResult sr : results)
   {
       if (sr.isSuccess())
       {
           System.debug('Successfully published event.');
       }
       else {
           for(Database.Error err : sr.getErrors())
           {
               System.debug('Error returned: ' + err.getStatusCode() + err.getMessage());
           }
       }
   }
 }
}

These records will be inserted synchronously. Event publishing is similar to DML operation and hence DML limits apply.

Events can be subscribed using triggers. After insert triggers can be used just like any other object type. After the event is published the after insert trigger is fired. Based on the conditions we can add business logic to suit your requirement.

trigger Newvacancy on Vacancy__e (after insert) {
    // Trigger for catching new vacancy events.
    // // Iterate through each notification.
    // // List to hold all Positions to be created.
     List<Position__c> pos = new List<Position__c>();
    for (Vacancy__e event : Trigger.New)
    {
        System.debug('Vacancy type: ' + event.Position_Type__c); 

            // Create a Position to indicate vacancy.
            Position__c p = new Position__c();
            p.Name = event.Position_Type__c;
            pos.add(p);
     } 

     // Insert all Positions corresponding to events received.
     // insert Position;
     insert pos;

}

In the above scenario, we have written after insert trigger. So, in that case if we have a new vacancy created then a corresponding record will be generated in object Position.

4.PNG

Here you see that a new record has been generated in Position object when a new Platform event is published. The record shows that it has been created by an Automated Process. Also the CreatedById and LastModifiedById references the Automated Process and not the current user

Only after insert triggers are supported as events cannot be updated once they are published. While inserting events through triggers due to infinite loop the daily limit for events could be exceeded.

Platform events aren’t processed with database transactions in Salesforce. Hence setSavepoint() and rollback() are not supported with published Platform events.

Platform events in the API

Records can be inserted, same like any other object. To create an event in REST API.
REST Endpoint is:
/services/data/v39.0/sobjects/Vacancy__e/
Request Body :
{
“Position_Type__c” : “Software Engineer”
}
The REST response looks something like:

5.PNG

Subscribe to Platform Events Using CometD

Consider a situation where you want to interact with external applications. Imagine you have an application in Heroku. When a  particular event takes place you want an event notification to be published to all your vendors.

In case of a merchandise app, suppose you want to purchase a particular item. After you have purchased the item and the opportunity closes in that case the app listens for an event and generated an event notification to all the associated vendors.

Subscribing to Platform events is similar to other sObjects.
The channel name will be /event/Event API Name

Step 1: Create a Platform event called Notification of the following type.

6.PNG

Step 2: Download the cometd static resource from CometD JavaScript client v3.1.1.
Create a static resource and name it as cometd (cache control: Public).

Step 3:  Create a lightning component that will display the notifications received in a toast

Lightning Component

<aura:component controller="NotificationController" implements="flexipage:availableForAllPageTypes" access="global">
<ltng:require scripts="{!$Resource.cometd}" afterScriptsLoaded="{!c.onCometdLoaded}"/>

<aura:attribute name="sessionId" type="String"/>
<aura:attribute name="cometd" type="Object"/>
<aura:attribute name="cometdSubscriptions" type="Object[]"/>

  <aura:attribute name="notifications" type="Object[]"/>
  <aura:attribute name="isMuted" type="Boolean" default="false"/>

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

  <aura:registerEvent name="toastEvent" type="force:showToast"/>
<div class="container">

    <!-- Header -->
<div class="slds-p-around--x-small slds-border--bottom slds-theme--shade">
<div class="slds-grid slds-grid--align-spread slds-grid--vertical-align-center">
<div>
          <span class="slds-badge">{!v.notifications.length}</span></div>
<div>
          	<lightning:buttonIcon onclick="{!c.onClear}" iconName="utility:delete" title="Clear notifications"             alternativeText="Clear notifications" variant="border-filled"/>
          	<lightning:buttonIcon onclick="{!c.onToggleMute}"             iconName="{!v.isMuted ? 'utility:volume_off' : 'utility:volume_high'}"             title="{!v.isMuted ? 'Unmute notifications' : 'Mute notifications'}"             alternativeText="Toggle mute" variant="border-filled"/></div>
</div>
</div>
<!-- Notification list -->
<div class="slds-container--fluid slds-scrollable--y content">
      <aura:iteration items="{!v.notifications}" var="notification">
<div class="slds-p-around--small slds-border--top">
<div class="slds-grid slds-grid--align-spread slds-has-flexi-truncate">

{!notification.message}

{!notification.time}</div>
</div>
</aura:iteration></div>
</div>
</aura:component>

Controller.js

({
  onCometdLoaded : function(component, event, helper) {

  var cometd = new org.cometd.CometD();
  component.set('v.cometd', cometd);
  if (component.get('v.sessionId') != null)
    helper.connectCometd(component);
},
  onInit : function(component, event, helper) {
  component.set('v.cometdSubscriptions', []);
  component.set('v.notifications', []);

  // Disconnect CometD when leaving page
  window.addEventListener('unload', function(event) {
    helper.disconnectCometd(component);
  });

  // Retrieve session id
  var action = component.get('c.getSessionId');
  action.setCallback(this, function(response) {
    if (component.isValid() && response.getState() === 'SUCCESS') {
      component.set('v.sessionId', response.getReturnValue());

      if (component.get('v.cometd') != null)
        helper.connectCometd(component);
    }
    else
      console.error(response);
  });
  $A.enqueueAction(action);

  helper.displayToast(component, 'success', 'Ready to receive notifications.');
},
  onClear : function(component, event, helper) {
    component.set('v.notifications', []);
    },

  onToggleMute : function(component, event, helper) {
    var isMuted = component.get('v.isMuted');
    component.set('v.isMuted', !isMuted);
    helper.displayToast(component, 'success', 'Notifications '+ ((!isMuted) ? 'muted' : 'unmuted') +'.');
    },

})

Helper.js

({
  connectCometd : function(component) {

    var helper = this;

    // Configure CometD
    var cometdUrl = window.location.protocol+'//'+window.location.hostname+'/cometd/40.0/';
    var cometd = component.get('v.cometd');
    cometd.configure({
      url: cometdUrl,
      requestHeaders: { Authorization: 'OAuth '+ component.get('v.sessionId')},
      appendMessageTypeToURL : false
    });
    cometd.websocketEnabled = false;

    // Establish CometD connection
    console.log('Connecting to CometD: '+ cometdUrl);
    cometd.handshake(function(handshakeReply) {
      if (handshakeReply.successful) {
        console.log('Connected to CometD.');
        // Subscribe to platform event
        var newSubscription = cometd.subscribe('/event/Notification__e',
          function(platformEvent) {
            console.log('Platform event received: '+ JSON.stringify(platformEvent));
            helper.onReceiveNotification(component, platformEvent);
          }
        );
        // Save subscription for later
        var subscriptions = component.get('v.cometdSubscriptions');
        subscriptions.push(newSubscription);
        component.set('v.cometdSubscriptions', subscriptions);
      }
      else
        console.error('Failed to connected to CometD.');
    });
      },

  disconnectCometd : function(component) {
    var cometd = component.get('v.cometd');

    // Unsuscribe all CometD subscriptions
    cometd.batch(function() {
      var subscriptions = component.get('v.cometdSubscriptions');
      subscriptions.forEach(function (subscription) {
        cometd.unsubscribe(subscription);
      });
    });
    component.set('v.cometdSubscriptions', []);

    // Disconnect CometD
    cometd.disconnect();
    console.log('CometD disconnected.');
  },

  onReceiveNotification : function(component, platformEvent) {
    var helper = this;
    // Extract notification from platform event
    var newNotification = {
      time : $A.localizationService.formatDateTime(
        platformEvent.data.payload.CreatedDate, 'HH:mm'),
      message : platformEvent.data.payload.Message__c
    };
    // Save notification in history
    var notifications = component.get('v.notifications');
    notifications.push(newNotification);
    component.set('v.notifications', notifications);
    // Display notification in a toast if not muted
    if (!component.get('v.isMuted'))
      helper.displayToast(component, 'info', newNotification.message);
  },

  displayToast : function(component, type, message) {
    var toastEvent = $A.get('e.force:showToast');
    toastEvent.setParams({
      type: type,
      message: message
    });
    toastEvent.fire();
  }
})

Apex Controller

public class NotificationController {

    @AuraEnabled
public static String getSessionId() {
  return UserInfo.getSessionId();
}

}

Step 4: From app manager go to Sales app with lightning enabled.
Click on Utility Bar tab.
Click on Sales. You will see a toast that displays “Ready to receive notification”.
Below you will see a screen that display your lightning component. Click on the tab to see the notifications received.

7.PNG

Step 5: We will test this application through a Heroku app Bear Watch Heroku app

8.PNG

Step 6: Click on the Broadcast Bear warning

9.PNG

Applications of Platform Events

1) A salesforce app that may notify about order fulfillment of a product
shipment order.
2) An external product app notifies in case of merchandise returns.
3) You are low on your Printer Ink a case can be generated to order new
cartridge.

Any many such business applications that will help to increase the efficiency and productivity through integration via events. After Summer’17 updates, Platform events are available for all with Enterprise Edition and higher. There are limits on event volume that can be increased with the help of add-ons. Platform events in future will provide deep integration with Heroku using Heroku Connect to provide additional processing power, additional protocols will be added along with High Volume Platform Events.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s