変更データキャプチャと非表示ユーリティティ項目を用いてプッシュ通知を実現する

Winter '19リリースで、**変更データキャプチャ**(Change Data Capture)が[Developer Previewになりました。](https://releasenotes.docs.salesforce.com/en-us/winter19/release-notes/rn_data_change_events.htm)変更データキャプチャは、指定したオブジェクトに対してレコードの作成・変更・削除をニアリアルタイムにSalesforceのイベントバスにPublishするサービスです。

イベントをSubscribeするには、これまでは独自でCometDクライアントを実装する必要がありましたが、こちらもWinter '19で**lightning:empApi**という便利なSubscribe用のコンポーネントAPIが登場しました。

Subscribeを実装したLightningコンポーネントを配置するにあたっては、またまたWinter '19から利用可能な**lightning:backgroundUtilityItem**インタフェースにより、Lightningコンポーネントを非表示のユーティリティバー項目として扱うことができます。 [^1]
[^1]: 余談ですがClassicのサービスコンソールでは設定画面でVisualforceのカスタムコンソールコンポーネントを非表示にするオプションがありました。

## 実装例
評価(Rating)項目がHotの取引先が作成された場合にトーストメッセージを表示してみましょう。

### 1. 変更データキャプチャの設定
[設定] - [インテグレーション] - [変更データキャプチャ] から対象のオブジェクトを選択し保存します。
<img width="1174" alt="Change Data Capture Setting" src="https://qiita-image-store.s3.amazonaws.com/0/184624/08dc10ef-7105-0694-84d2-a8c120491ce2.png">

### 2. Lightningコンポーネントの作成
```html:StreamingUtilityItem.cmp
<aura:component implements="lightning:backgroundUtilityItem">
	<lightning:empApi aura:id="empApi"/>
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
</aura:component>
```

```javascript:StreamingUtilityItemController.js
({
    doInit : function(component, event, helper) {
        const empApi = component.find("empApi");
        const channel = "/data/AccountChangeEvent";
        const replayId = -1; //登録後に発生したすべての新規イベントを受信
        
        // イベント受信時の処理
        const onMessageCallback = (message) => {
            console.log("Received [" + message.channel +
            " : " + message.data.event.replayId + "] payload=" +
            JSON.stringify(message.data.payload));
            helper.notify(message.data.payload);
        };
            
        // Subscribeの開始 (valueはSubscribeしたオブジェクト)
        empApi.subscribe(channel, replayId, onMessageCallback).then((value) => {
            console.log("Subscribed to channel " + channel);
        });    
            
        // エラー処理
        empApi.onError((message) => {
            console.log("Received error ", message);
        });  
    }
 })
```

```javascript:StreamingUtilityItemHelper.js
({
	notify : function(payload) {
        if(payload.ChangeEventHeader.changeType == 'CREATE' && payload.Rating == 'Hot'){
            $A.get("e.force:showToast").setParams({
                type: 'success',
                message: 'Hotな取引先 ' + payload.Name +' が作成されました！'
            }).fire();
        }
	}
})
```
#### ポイント
* 変更データキャプチャのチャネルは標準オブジェクトの場合、``/data/<sObject_Name>DataChange``となります。カスタムオブジェクトの場合は``/data/<CustomObject_Name>__ChangeEvent``です。
* replayIdの設定については[Streaming API開発者ガイド - メッセージの永続性](https://developer.salesforce.com/docs/atlas.ja-jp.214.0.api_streaming.meta/api_streaming/using_streaming_api_durability.htm)も参照してください。
* 変更データキャプチャでは以下のようなイベントを受信します。作成時はシステム項目および入力値のある項目が、更新時は値の変更がある項目だけがペイロードに含まれますので注意が必要です。（当たり前ではありますが）
* 
```json:
{
  "data": {
    "schema": "IeRuaY6cbI_HsV8Rv1Mc5g", 
    "payload": {
      "ChangeEventHeader": {
        "entityName": "Account", 
        "recordIds": [
          "001R0000002aV0B"
        ], 
        "changeType": "CREATE", 
        "changeOrigin": "com.salesforce.core", 
        "transactionKey": "001b7375-0086-250e-e6ca-b99bc3a8b69f", 
        "sequenceNumber": 1, 
        "isTransactionEnd": true, 
        "commitTimestamp": 1501010206653, 
        "commitNumber": 92847272780, 
        "commitUser": "<User_ID>"
      }, 
      "Name": "Acme", 
      "Description": "Everyone is talking about the cloud. But what does it mean?", 
      "OwnerId": "<Owner_ID>", 
      "CreatedDate": "2017-07-25T19:16:44Z", 
      "CreatedById": "<User_ID>", 
      "LastModifiedDate": "2017-07-25T19:16:44Z", 
      "LastModifiedById": "<User_ID>"
    }, 
    "event": {
      "replayId": 6
    }
  }, 
  "channel": "/data/ChangeEvents"
}
```

* イベントではレコードIDが複数形(recordIds)となっていますが、試しにListをinsertしたところレコードの数だけイベントを受信しました。1イベントに複数のIdが含まれる条件をご存知の方はぜひ教えてください。


### 3. アプリケーションへコンポーネントの配置
アプリケーションマネージャのユーティリティ項目で作成したコンポーネントを選択し保存します。
<img width="818" alt="App Manager" src="https://qiita-image-store.s3.amazonaws.com/0/184624/5b0341fe-62f7-a8c6-6e65-cbedd5baa592.png">



## 動作確認
AccountをInsertし、別ウィンドウで通知を待ってみました。うまく動いていますね！
![Change Data Capture Notify Demo.gif](https://qiita-image-store.s3.amazonaws.com/0/184624/0b994439-cd89-3b86-806f-7dea8cc41fc5.gif)

## まとめ
変更データキャプチャ・lightning:empApi・lightning:backgroundUtilityItemを組み合わせることで、簡単に通知機能を実装できました。

変更データキャプチャはPushTopicやApexトリガ＋Platform Eventよりも使いやすく、外部システムとの連携など多くの活用方法がありそうです。lightning:empApiでイベントのSubscribeが実装しやすくなりました。lightninbg:backGroundUtilityItemも通知以外の活用方法がたくさんありそうですね！Enjoy Winter '19!

### 参考リンク
* lightning:empApiのリファレンス : /docs/component-library/bundle/lightning:empApi/documentation
* lightning:backgroundUtilityItemのリファレンス : /docs/component-library/bundle/lightning:backgroundUtilityItem/documentation
* [CometDクライアントの実装例 (Trailhead - インスタント通知アプリケーションの作成 > イベントの登録](https://trailhead.salesforce.com/ja/projects/workshop-platform-events/steps/platform-event-subscribe)
* [Andrew FawcettさんのStreaming API Lightningコンポーネント](https://github.com/afawcett/streamingcomponent)
* [Streaming API開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.api_streaming.meta/api_streaming/)
* [Platform Event開発者ガイド](https://developer.salesforce.com/docs/atlas.ja-jp.platform_events.meta/platform_events/)