Monday, March 21, 2016

[Salesforce / Streaming API / On Demand] Streaming API Notification Lightning Component

UPDATE: With Winter '15 you no longer can use a lightning context Session ID to make REST API calls, this component no longer works. Read Session Privilege Levels.

Few weeks ago I asked the community this simple question:


The Salesforce Community is awesome and suddenly I had cool answers.

I concentrated on the first one:


I didn't want to simply explain how Streaming APIs work and at the same time I wanted to do something fun and while I was walking with my dog I thought:


If you have any idea please send me awesome and challenging on demand articles topics!

For TL;DR buddies, here is the complete GitHub repository.

First of all here is the complete guide on Salesforce Streaming APIs.

As usual, this is a "quick and dirty" blog post, so don't expect me to explain how the magic works.

Streaming API is a cool set of APIs which allow a client to be notified from the server when a specific event occurs.

It works like push notifications and uses long polling, in the sense that the client polls for updates from the server but the server doesn't answer back till the next event (the connection remains idle), and when the client receives finally the response it usually makes another logn polling request, and so on.

This is what happens:
  1. client connects to the server (handshake)
  2. client subscribes to a specific server event (called channel)
  3. client listens to that event using long polling
  4. server sends event
  5. client consumes the event
  6. back to step 2

This technology is based on the Bayeux protocol and supports the dialect of the CometD 2.0 library (that implements the Bayeaux protocol).

Here is some characteristics of the flow:

  • Maximum "client to server" post body size is 32,768 bytes (e.g. connect or subscribe methods)
  • After an event is received, the client has to reconnect withing 40 seconds or the server closes the connection and a new handshake is necessary
  • In case of network problems, the client should attempt an automatic reconnection and a new subscription
  • Client can subscribe to the meta/handshake channel to receive informations about the connection

Remember that "Streaming API doesn’t guarantee durability and reliable delivery of notifications": the server does'nt keep any state so it doens't keep track of what's delivered.

Here is a simple guide of using Streaming API with Workbech.

Enable a PushTopic


The PushTopic SObject is the mean for creating a Streaming API channel.

It hosts the query that originates the events sent to the subscribers.

These are the PushTopics we are going to use in our project:

PushTopic pushTopic1 = new PushTopic();
pushTopic1.Name = 'Opportunity_Won';
pushTopic1.Query = 'SELECT Id, Amount, Name FROM Opportunity Where StageName=\'Closed Won\'';
pushTopic1.ApiVersion = 36.0;
pushTopic1.NotifyForOperationCreate = true;
pushTopic1.NotifyForOperationUpdate = true;
pushTopic1.NotifyForFields = 'Where';
insert pushTopic1;

pushTopic = new PushTopic2();
pushTopic2.Name = 'Lead_Converted';
pushTopic2.Query = 'SELECT Id, Name,Company FROM Lead Where IsConverted = true';
pushTopic2.ApiVersion = 36.0;
pushTopic2.NotifyForOperationCreate = true;
pushTopic2.NotifyForOperationUpdate = true;
pushTopic2.NotifyForFields = 'Where';
insert pushTopic2;

The drivers of a PushTopic are:
  • Query: this is the query used to create the data pool
  • Operations: when a record has to scanned by the engine, that is on create, update, delete, udelete
  • Fields: the record is scanned when fields change if they are in the WHERE clause, SELECT clause, REFERENCE (where+select) or ALL (all fields changes trigger the PushTopic, if the where clause still apply)

In our examples we are subscribing to the create/update operations on the leads and opportunity when leads are converted and opportunities are closed and won.

Users will receive a notification if FLS (field level security) allow them to view the topic fields, if they have read access to the record and to the PushTopic, visibility on the record based on the sharing rules.

Limitations


The following objects can be used in the PushTopi query:
  • All Custom Objects
  • Account
  • Campaign
  • Case
  • Contact
  • Lead
  • Opportunity
  • Task (with few limitations)

And here you are a list of unsupported SOQL statements.

There are also a number of limits all exposed in this page.

The Lightning playroom


The GitHub repository provides the following components:

  • svg: replicates the svg HTML5 element not currently supported in Lightning components (outputs icons and stuff)
  • UINotification: used to display a alert/toast notification when a specific event occurs. You can choose the style (info, error, success, warning), type (Toast, Alert) and the timer in seconds for auto-hiding, and ofcourse a title and a content detail
  • StreamingAPIEvent: event fired when a specific channel sends an event
  • ShowNotificationEvent: event fired whan a Streaming API event is consumed and allow for displaying a notification
  • StrApiListener: subscribes client to one or several topics (only one component is allowed per app); it fires a StreamingAPIEvent event.
  • StrApiTopicTemplate: (global component) listens to the StreamingAPIEvent event, grabs its content, compile its templates for title and content and fires a ShowNotificationEvent to the unique notification center
  • StrApiNotificationDock: (global component) hosts the StrApiListener component and handles ShowNotificationEvent events, dynamically creating UINotification component

The StrApiTestApp hosts the following components instances:

<aura:application>
    <!-- ... -->

    <c:StrApiNotificationDeck topics="Opportunity_Won,Lead_Converted"
        apiVersion="36" 
        notificationType="Alert" />
    
    <c:StrApiTopicTemplate topic="Lead_Converted"
                                 style="warning" 
                                 title="Created new lead called {{Name}}"
                                 content="Company: {{Company}}"/>
    <c:StrApiTopicTemplate topic="Opportunity_Won"
                                 style="success" 
                                 title="Won $ {{Amount}} !"
                                 content="Name is {{Name}}" />
</aura:application>

The StrApiNotificationDeck subscribes to the Opportunity_Won and Lead_Converted PushTopics using an Alert notification style.

For each channel defines a StrApiTopicTemplate template with a proper notification style, title and content.

Title and Amount attributes can be templates referring to the corresponding SObject's fields wrapped by the double moustaches ({{Field__c}}).

Run to https://xxx.lightning.force.com/c/StrApiTestApp.app:



Lightning App Builder


The StrApiNotificationDeck and StrApiTopicTemplate components are defined as global and implements the flexipage:availableForAllPageTypes interface so they are good to go with the Lightning App Builder.

Compoments are invisible so you are not expected to see anything on the builder: as far as I know there is not way to create a sort of "default" view for the builder.


And this is your Salesforce 1 brand new app:


Custom Streaming Channels


The Force.com platform allows for custom channels.

They can be access by:
  • Creating a new StreamingChannel object
  • Sending a notification to the channel using the Streaming Channel REST API

To create a new StreamingChannel go to All Tabs > Streaming Channel and create a new streaming channel:


The Name must include the "/u/" prefix. You can specify whichever path you like (e.g. /u/users/alerts, /u/sales/notifications).

The components we have creates supports this new kind of channel:

<c:StrApiNotificationDeck topics="Opportunity_Won,Lead_Converted,/u/CustomChannel"
        apiVersion="36" 
        notificationType="Alert" 
        hideAfter="5"/>
    
    <!-- . . . -->

    <!-- custom streaming channel -->
    
    <c:StrApiTopicTemplate topic="/u/CustomChannel"
                                 style="info" 
                                 title="Message received"
                                 content="{{$}}" />
    

The template component supports the {{$}} value that stands for "the whole data".

Indeed the StreamingChannel supports only strings.

To execute a REST push call follow the following instructions, or manually execute the following call:

Header:
"method":"POST",
    "uri":"https://[YOUR_DOMAIN].salesforce.com/services/data/v36.0/sobjects/StreamingChannel/[STREAMING_CHANNEL_ID]/push",
    "headers":[
        {
            "key":"Authorization",
            "value":"Bearer [YOUR_SESSION_ID]"
        },
        {
            "key":"Content-Type",
            "value":"application/json"
        }
    ]

Body:
{ 
      "pushEvents": [
          { 
              "payload": "Broadcast message!!!!",
              "userIds": [] 
          },{ 
              "payload": "This message is only for Enreeco!!!",
              "userIds": ["00524000000eaVt"] 
          } 
       ] 
    }

The messages are composed by:

  • payload: the message to be sent (plain text)
  • userIds: leave empty Array for broadcast message, of add one or more user Ids


May the Force.com be with you!


Thursday, March 3, 2016

[Salesforce / Apex] Let's play with Named Credentials and OAuth 2.0

Few days ago I was lurking in the Named Credentials configurations.

What are named credentials?

Here are the official docs.

They are essentially a way to store callout configurations such as:

  • Endpoint (only HTTPs endpoints are supported)
  • Callout certificate (if needed, from the local key store)
  • Authentication protocol (if needed)
  • Authentication settings

With named credentials you don't need Remote Site Setting configuration anymore: add you credential and the job is done!

The Endpoint stores the callout URL but it could also store a part of the endpoint (e.g. only the domain or a specific piece of path, such as https://na1.salesforce.com/services/Soap/class/).

The Callout certificate grabs a certificate from the Certificate and Key Management setup page, allowing a more secure connection between hosts.

I'm going to focus on the Authentication protocol.


You can choose among the following values:
  • No Authentication
  • Password authentication
  • OAuth 2.0

And you can set the authentication globally (Identity type = Named principal) or per user (Identity Type = Per User): if you don't need any authentication, just use the value "Identity Type = Anonymous"

The password authentication is pretty straightforward: it uses BASIC authentication (username/password BASE64 encoded sent in the request headers).

But what about OAuth2?


I wanted to test this option using all but the Salesforce platform: there are plenty of services that expose the OAuth 2.0 authentication, but I love Salesforce and that's how I want to test things!

You need 2 orgs:
  • Remote ORG: this will be the provider of your OAuth 2.0 authentication, it hosts the remote service you want to access (and that you can only access with this ORG's user)
  • Local ORG: this is the ORG where you want to invoke the remote service and where you are configuring the Named Credentials

Setup the Remote ORG


First let's setup the remote service.

Create a new SOAP webservice:

global class EchoManager {
    webservice static String echo(String text){
        return 'ECHO FROM ORG '+UserInfo.getOrganizationId()+': '+text;
    } 
}

This service echoes the request text's returning also the remote ORG ID.

Useful uh?

Go to Setup > Develop > Apex Classes search the EchoManager class and click the WSDL link next to the class name: this way you can download the WSDL file you are going to import in the Local ORG to call the service.

Make sure the remote user you'll be using (with OAuth2) to consume the service from the Local ORG is enabled to access the Apex Class (from its Profile or assigning a Permission set: if this is an Administrator profile no need to check!).

Last step on this org is to setup a Connected App: this confgiuration will give access to the ORG from outside on behalf of an external application.

Go to Setup > Create > Apps, scroll to the Connected Apps section and click the New button:


Setup a name, a contact Email, a logo image (I choose Master Yoda).

Flag Enable OAuth Setting and set Callback URL to a fake value (e.g. callback://myapp): we'll be configuring this field later (this hosts the callback url of the Local ORG).

Finally set the full and refresh_token scopes (the last one is necessary to allow for sending refresh token).

Onve you save it can take 5/10 minutes for the changes to be propagated.

This is pretty much what you see now (except for the Callback URL):


Let's go back to the Local ORG.

Setup the Local ORG


First let's import the remote service WSDL from Setup > Develop > Apex Classes and click the Generate From WSDL button.

Select the WSDL file just saved from the Remote ORG, use a namespace name (WSEchoManager in my case) and Salesforce will create the Apex Stub.

Let's create a Visualforce page with a controller to test it:

public class EchoController {
    public String requestText{get;set;}
    public String responseText{get;set;}
    public void sendRequest(){
        this.responseText = null;
        try{
            WSEchoManager.EchoManager stub = new WSEchoManager.EchoManager();
            this.responseText = stub.echo(this.requestText);
        }catch(Exception e){
            this.responseText = 'Unexpected exception :'+e.getMessage();
        }
    }
}

<apex:page controller="EchoController" tabStyle="Account">
    <apex:sectionHeader title="Remote echoes" />
    <apex:form>
        <apex:pageBlock>
            <apex:pageBlockSection columns="2">
                <apex:pageBlockSectionItem>
                    <apex:outputLabel>Say something:</apex:outputLabel>
                    <apex:inputText value="{!requestText}" />
                </apex:pageBlockSectionItem>
                <apex:commandButton value="Send request" action="{!sendRequest}" />
            </apex:pageBlockSection>
            <apex:pageBlockSection columns="1" 
                                   title="Server response" 
                                   rendered="{!NOT(ISBLANK(responseText))}">
                <apex:outputText value="{!responseText}" />
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>

Let's try it:


As expected we haven't still enabled anything.

Jump to Setup > Security Controls > Auth. Providers and click the New button and choose the Salesforce provider type:


This is the authentication provider will be using to initiate the OAuth 2 dance with the Remote ORG.

In the Consumer Key and Consumer Secret fields set the values from the Remote ORG connected app (called Consumer Key and Secret as well).

The Authorization Endpoint URL and Token Endpoint URL are setup with the https://login.salesforce.com/services/oauth/... values, but in this case I've used the Remote ORG custom domain (you can leave with the default values).

In the Default Scopes set the selected scopes in the Remote ORG connected app separated by a blank space (full refresh_token).

Now click "Save" and get the Callback URL you get in the Salesforce Configuration section:


Go back to the Remote ORG connected app and set the Callback URL with the one just copied.

We are about there, don't worry!


Go back to your Local ORG and go to Setup > Security Controls > Named Credentials and click New.


Give a fantastic name to your named credential (you'll be using it in your stub class), set the URL with the service url you find in the WSEchoManager class:

 . . .
    public class EchoManager {
        public String endpoint_x = 'https://eu6.salesforce.com/services/Soap/class/EchoManager';
        public Map<String,String> inputHttpHeaders_x;
    . . .

Set the Named Principal Identity Type and the OAuth 2.0 protocol; select the Authentication Provider just configured and flag the Start Authentication Flow on Save: this way, upon saving the named credential, you are requested to access to the remote provider.

Flag Allow Merge Fields in HTTP Body as well in order to put the OAuth token inside the request in a "non standard" way (we dont' want to use the Authorication: Bearer XXX header).


Cheers: you have a working connection!


Finally we have to change the stub code to recall the named credential:

 . . .
    public class EchoManager {
        public String endpoint_x = 'callout:Echo_Service';
        . . .
        public String echo(String text) {
            WSEchoManager.echo_element request_x = new WSEchoManager.echo_element();
            request_x.text = text;
            this.SessionHeader = new SessionHeader_element();
            this.SessionHeader.sessionId = '{!$Credential.OAuthToken}';
        . . .

We are using the Echo_Service named credential in the endpoint_x member and adding the SessionHeader parameters in the SOAP request using the named credential merge field {!$Credential.OAuthToken}, which stores the token needed to authorize the call.

Thanks to the Allow Merge Fields in HTTP Body the engine replaces automagically this string with the session ID referenced by the merge field inside the SOAP request...and:



You can find the full code in the following Github repository.