プラットフォームイベントをつかった通知アプリ開発を試してみました。マイドメインが有効化されたDE組織が必要になります。
プラットフォームイベントを定義
設定から新規作成できます。
作成画面はこんな感じです。
API名は「__e」になります。
カスタム項目を作成します。
Lightningコンポーネント作成
notificationConsole.cmpを作成します。
<aura:component implements="flexipage:availableForAllPageTypes" access="global"> <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"> <p>{!notification.message}</p> <p class="slds-text-color--weak slds-p-left--x-small">{!notification.time}</p> </div> </div> </aura:iteration> </div> </div> </aura:component>
Controllerを作成します。
({ onInit : function(component, event, helper) { component.set('v.notifications', [ {time: '00:01', message: 'Greetings Trailblazer!'}, {time: '00:02', message: 'Congratulations on building this first version of the app.'}, {time: '00:03', message: 'Beware of the bears.'} ]); 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を作成します。
({ displayToast : function(component, type, message) { var toastEvent = $A.get('e.force:showToast'); toastEvent.setParams({ type: type, message: message }); toastEvent.fire(); } })
Styleを作成します。
.THIS.container { height:100%; } .THIS .content { height:calc(100% - 49px); }
Lightningコンソールの作成
App ManagerにアクセスしてSalesアプリケーションを編集する形で進めます。(Classicアプリケーションでは作業不可)
Utility Bar設定で先程のLightningコンポーネントを追加します。
Cometdのダウンロード
下記リンク先からダウンロードできます。
静的リソースへアップします。
NotificationControllerの作成
NotificationController.clsを作成します。
public class NotificationController { @AuraEnabled public static String getSessionId() { return UserInfo.getSessionId(); } }
Lightningコンポーネントの修正
<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"> <p>{!notification.message}</p> <p class="slds-text-color--weak slds-p-left--x-small">{!notification.time}</p> </div> </div> </aura:iteration> </div> </div> </aura:component>
Helperの修正
({ 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(); } })
Controller.jsの修正
({ 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.'); }, 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); }, 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') +'.'); } })
これでコーディング部分は作業完了です。
動作確認
Bear Watch Heroku appというサイトから動作確認できます。
Loginボタンをクリックしてアクセスの承認します。
ボタンをクリックすると処理が実行されます。
このようにSalesforce側の通知処理が実行できました。
処理が動かない場合は『cmd + shift + r』のキーで画面を再描画すると解決すると思います。
Apexトリガをつかった通知
NotificationController.clsの処理を変更します。
public class NotificationController { @AuraEnabled public static String getSessionId() { return UserInfo.getSessionId(); } public static void publishNotifications(List<String> messages) { List<Notification__e> notifications = new List<Notification__e>(); for (String message: messages) { notifications.add(new Notification__e(Message__c = message)); } List<Database.SaveResult> results = EventBus.publish(notifications); // Inspect publishing results for (Database.SaveResult result : results) { if (!result.isSuccess()) { for (Database.Error error : result.getErrors()) { System.debug('Error returned: ' + error.getStatusCode() +' - '+ error.getMessage()); } } } } }
TopicAssignmentオブジェクトのトリガを作成します。
trigger BearAlertTopicAssignmentTrigger on TopicAssignment (after insert) { // Get FeedItem posts only Set<Id> feedIds = new Set<Id>(); for (TopicAssignment ta : Trigger.new){ if (ta.EntityId.getSObjectType().getDescribe().getName().equals('FeedItem')) { feedIds.add(ta.EntityId); } } // Load FeedItem bodies Map<Id,FeedItem> feedItems = new Map<Id,FeedItem>([SELECT Body FROM FeedItem WHERE Id IN :feedIds]); // Create messages for each FeedItem that contains the BearAlert topic List<String> messages = new List<String>(); for (TopicAssignment ta : [SELECT Id, EntityId, Topic.Name FROM TopicAssignment WHERE Id IN :Trigger.new AND Topic.Name = 'BearAlert']) { messages.add(feedItems.get(ta.EntityId).body.stripHtmlTags().abbreviate(255)); } // Publish messages as notifications NotificationController.publishNotifications(messages); }
これで準備完了です。Chatterに下記テキストを投稿します。
#BearAlert False alarm: It’s just a big dog!
先ほどと同じように通知が表示されます。
以上がプラットフォームイベントをつかった通知処理です。ユーティリティバーの通知はLightnign Experienceのみ利用可能となりますがすごく便利そうでした。