+1-310-929-7392 info@springsoa.com

Was just working on streaming API and just putting thoughts together with different variations of streaming API :

  1. Push Topic
  2. Generic Topic
  3. Platform Event

Even though underneath the cover, they all use same technology stack, they provide very different features.

Push Topic Generic Topic Platform Events
Overview Push topic is used for SOQL based subscription Generic Topic is used to subscribe and publish arbitrary events
Platform Events is used for structured
publish and subscribe
There is a lot more native support for both
publish and subscribe
Full control over structure (payload) of the
event
Replay :
When client disconnect and reconnect again, they can replay from last 24 hours with id where they left from
special ids : -1 from the beginning, -2 : all new events
Supported (version 36.0 +) Supported (version 36.0 +) Supported (version 36.0 +)
Create (Setup)
1) Using Apex Insert Statements
or
2) workbench
– it defaults all the param except SOQL query
Using Streaming Channel Tab
or
2) Workbench
Create __e object
Support for Trigger Subscription No No Yes
How to Publish upon SOQL Using Rest API
EventBus.publish;
API : Post like sobject /services/data/v41.0/sobjects/Low_
Ink__e/
Process Builder
Flow
How to Subscribe
workbench (36.0) (later are causing problems)
using Java (cometd lib)
Using Javascript (cometd lib)
workbench
using Java (cometd lib)
Using Javascript (cometd lib)
using Java (cometd)
Using Javascript (cometd)
Using Visualforce (cometd)
Process Flow
Flow
Trigger
Workbench
Channel Name
This is useful when we subscribe using cometd library
/topic/<>
e.g.
/topic/AccountUpdatePushTopic
/u/<>
e.g.
/u/GenericUpdateTopic
/event/<>
e.g.
/event/UpdateObjectEvent__e
Push Topic

Push Topic Setup

Workbench
we can setup a push topic using workbench, but it doesn’t allow granular control over topic configuration.

APEX

  • NotifyForFields can be Referenced (part of query – default), Where (in where condition), All (all changes)
  • Once topic is inserted, we can use PushTopic object to make any update
 PushTopic pushTopic = new PushTopic();  
 pushTopic.Name = 'AccountUpdatePushTopic';  
 pushTopic.Query = 'SELECT Id, Name, AccountNumber from Account';  
 pushTopic.ApiVersion = 40.0;  
 pushTopic.NotifyForOperationCreate = true;  
 pushTopic.NotifyForOperationUpdate = true;  
 pushTopic.NotifyForOperationUndelete = true;  
 pushTopic.NotifyForOperationDelete = true;  
 pushTopic.NotifyForFields = 'Referenced';  
 insert pushTopic;  
   
 

Push Topic Publish

There is no API support for publish. Any change in the data based on condition (e.g. Notify Operation and fields configured) would fire the event on the topic.

 

Push Topic Subscribe

Workbench

  • This is quick way to test and also record the channel, as we will need it later for cometd library

Java/JavaScript
covered later

 
 

Generic Topic


 

Generic Topic Setup

  • We must setup generic toipc using salesforce UI
  • There is no apex or workbench support to create generic topic

 

 
Generic Topic Publish

Rest API (Workbench)

URL : /services/data/v/sobjects/StreamingChannel//push
We can find streaming channel id using query (SELECT Name, ID FROM StreamingChannel)
e.g. /services/data/v40.0/sobjects/StreamingChannel/0M61I000000TN1FSAW/push
payload : 
{
  “pushEvents”: [
      {
          “payload”: “Broadcast message to all subscribers”,
          “userIds”: []
      }
   ]
}

 

Generic Topic Subscribe

WorkBench

Java/JavaScript:
Covered Later

 
 

Platform Events


Platform Event Setup

  • Essentially created two attributes (ObjectName__c and RecordId__c) so that we can publish the event having those attributes

 

 
Platform Event Publish

Apex

  • Publish event call doesn’t fail, hence we have to go through the results
  • Also publish call doesn’t participate in transaction, meaning if transaction has to fail, event will still publish

 

 List events = new List();  
 UpdateObjectEvent__e event = new UpdateObjectEvent__e(objectName__c='Account', recordId__c='1234');  
 events.add( event );  
 List results = EventBus.publish(events);  
 if( results != null && results.size() > 0 ) {  
   for (Database.SaveResult sr : results) {  
     if (sr.isSuccess()) {  
       System.debug('Successfully published event. ' + results.size() );  
     } else {  
       for(Database.Error err : sr.getErrors()) {  
         System.debug('Error returned: ' + err.getStatusCode() + ' - ' + err.getMessage());  
       }  
     }  
   }  
 } else {  
   System.debug(' Noting is published. ');  
 }  
   



Rest API

Endpoint  : /services/data/v40.0/sobjects/UpdateObjectEvent__e/
Payload    : { “RecordId__c” : “123455634343”, “ObjectName__c” : “Account” }

Soap API
Similar to rest api, it is just call to insert into sObject

Process Builder 
not covered, but straightforward

Visual Flow 
not covered, but straightforward


Platform Event Subscribe

Trigger

  • only after insert is supported
 trigger UpdateObjectEventTrigger on UpdateObjectEvent__e (after insert) {  
   System.debug(' UpdateObjectEventTrigger ' );  
     
   for (UpdateObjectEvent__e event : Trigger.New) {  
     System.debug(' : ' + event.RecordId__c + ' ' + event.ObjectName__c );  
   }  
     
 }  

Process Builder 
not covered, but straightforward

Visual Flow 
not covered, but straightforward

Java/JavaScript 
Covered later

Platform Event Debug

  • debug statements in trigger doesn’t show up in debug logs, we need to enable them using below





Generic Java Subscriber Client


We need to download and build the EMP connector from salesforce
  • download : https://github.com/forcedotcom/EMP-Connector
  • unzip, and run “mvn clean install”
  • Use emp-connector-0.0.1-SNAPSHOT-phat.jar in the new project that we are going to create
  • Create a new project (java 1.8) and use below code.
  • Please note that channel name can be changed as per which topic we are subscribing
 package com.spring.client;  
   
 import com.salesforce.emp.connector.BayeuxParameters;  
 import com.salesforce.emp.connector.EmpConnector;  
 import com.salesforce.emp.connector.TopicSubscription;  
   
 import java.util.Map;  
 import java.util.concurrent.TimeUnit;  
 import java.util.function.Consumer;  
   
 import static com.salesforce.emp.connector.LoginHelper.login;  
   
 public class StreamingClient {  
   
   public static void main(String args[]) throws Exception {  
     long replayFrom = EmpConnector.REPLAY_FROM_EARLIEST;  
     BayeuxParameters params = login("streaming@springsoa.com", "Welcome1");  
     EmpConnector connector = new EmpConnector(params);  
     Consumer<Map<String, Object>> consumer = event -> System.out.println(String.format
("Received:\n%s", event));  
     connector.start().get(5, TimeUnit.SECONDS);  
     TopicSubscription subscription = connector.subscribe("/event/UpdateObjectEvent__e", 
replayFrom, consumer ).get(5, TimeUnit.SECONDS);  
     System.out.println(String.format("Subscribed: %s", subscription));  
     //subscription.cancel();  
     //connector.stop();  
   }  
 }  
   

Generic Javascript Subscriber Client


We can use cometD library to listen to the event, I had to write customer wrapper (cometdCustom.js) to greatly simply the visualforce page. We can also use this in independent HTML page,as long as we can get oauth session id.
 

Source Code


Java code                       :   https://github.com/c-shah/streaming-java-client
Salesforce and JS code  :   https://github.com/c-shah/salesforce-streaming

Refresh VisualForce Page with Platform Events

Problem Statement 

A visual force page is embedded inside the standard page layout to display additional information from third party application as below.

A few use cases :
– Let’s say there is change inside third party application content and we want to refresh the entire page
– If third party application is making change to sales force data on this page using API and we need to refresh the page



A few failed solutions

1) If third party application is rendered inside the iframe, we can not access the parent salesforce page.

E.g. if we try one of the below we get the error message, as salesforce will prevent third party iframe to access the parent page.

 window.parent.location.href = URL  
 parent.location.href=parent.location.href  
 parent.location.reload();  
 window.parent.location.href = window.parent.location.href  

error message:

 Unsafe JavaScript attempt to initiate navigation for frame with URL 'https://c.na59.visual.
force.com/servlet/servlet.Integration?lid=066f4000001yE8F&ic=1&...'.   
 The frame attempting navigation is neither same-origin with the target, nor is it the target's 
parent or opener  

2) Polling : in parent visualforce page we can constantly do polling on server side to listen for changes and refresh the VF page as needed. Polling in general is not good idea and not very scalable.


Platform Event to Rescue

Event based solution comes quite handy here, where we can publish the event on server side and visualforce page can listen to event and on right criteria, it can alert the end user on change or refresh the page so that we can see the fresh data.

Platform event is glorified version of streaming api, and we can get more details on a separate post here.



Solution:

1. Create platform event
2. Upon change, publish event using rest api or on Apex
3. On visualforce page, listen to those event and refresh the page



Create platform event 

  • Essentially created two attributes (ObjectName__c and RecordId__c) so that we can publish the event having those attributes



Publish event using rest api or on Apex



1) Publish via Apex
  • Publish event call doesn’t fail, hence we have to go through the results
  • Also publish call doesn’t participate in transaction, meaning if transaction has to fail, event will still publish
 List<UpdateObjectEvent__e> events = new List<UpdateObjectEvent__e>();  
 UpdateObjectEvent__e event = new UpdateObjectEvent__e(objectName__c='Account', recordId__c='1234');  
 events.add( event );  
 List<Database.SaveResult> results = EventBus.publish(events);  
 if( results != null && results.size() > 0 ) {  
   for (Database.SaveResult sr : results) {  
     if (sr.isSuccess()) {  
       System.debug('Successfully published event. ' + results.size() );  
     } else {  
       for(Database.Error err : sr.getErrors()) {  
         System.debug('Error returned: ' + err.getStatusCode() + ' - ' + err.getMessage());  
       }  
     }  
   }  
 } else {  
   System.debug(' Noting is published. ');  
 }  
   


2) Publish via Rest API

Endpoint  : /services/data/v40.0/sobjects/UpdateObjectEvent__e/
Payload    : { “RecordId__c” : “123455634343”, “ObjectName__c” : “Account” }




Listen to event on Visualforce Page 

 
We can use cometD library to listen to the event, I had to write customer wrapper (cometdCustom.js) to greatly simply the visualforce page.
 
 
 

Source code 
It can be found at : https://github.com/c-shah/salesforce-streaming