SalesforceでSlack通知をしようとしたときに躓いたこと

最近 Salesforce を導入しましたが、
業務に関する各種通知をSlackで行っているため Salesforce から Slack 通知するまで、右も左もわからない状態からなんとかできました。

今では通知する種類も増え、構造は違っていますが（Webhook使ってないなど）参考にはなると思います。

基本的に[ここ](https://trailhead.salesforce.com/ja/content/learn/modules/declarative-change-set-development)を参考にしています。

# sandbox環境を作成

Salesforce上でなにか処理を行うにはApexという言語で記述する必要があります。
しかし、本番環境では新規にApexクラスを追加できません。

Sandbox環境を作り、そこに追加したApexクラスを本番にDeployという形で本番に適用する必要があります。

設定のクイック検索で `sandbox` と検索します。
環境 -> sandbox という感じのメニューになるので、選択するとsandboxの一覧が確認できます。

![スクリーンショット 2019-10-02 17.32.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/59acb904-b2ec-3200-94a4-964d35768c64.png)

すでにdevelopというものを作ってしまっていますが、新規sandboxから新しいものも作れます。
基本的に動作確認などはSandbox環境で行います。

# Slack通知カスタムオブジェクトの作成

WebhookでのSlack通知用のオブジェクトを作成します。
オブジェクトマネージャ から作ります。

![スクリーンショット 2019-10-02 17.36.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/ee1bdfe4-5930-4022-07eb-1c10432f923e.png)

こんな感じのものを作りました。
URLがあるのは2種類の鹿島さんが存在する（請求情報変更・請求の確定通知）ので持たせています。
Webhookに渡す内容が `{ "text": "~~~~~"}` という感じなので、Textも用意しています。
`__c` が気になりますか？僕はもう気にならなくなりました。

# セキュリティ設定

今回は外部に通信を行うのでそういった設定が必要になります。
クイック検索に `リモートサイト` と入力して `リモートサイト設定` を行います。

![スクリーンショット 2019-10-02 18.58.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/b17dacd9-d3c1-ddcb-2453-4946be8f8a1f.png)

こんな感じで設定しています。
これをしないと外部に接続できないので注意しましょう。

# Slack送信部分の実装

クイック検索に「Apex」と入力すると

![スクリーンショット 2019-10-02 17.41.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/51c2c5e7-f0ab-e291-cbdb-3202d3dadbc8.png)

こんな感じになります。
「Apexクラス」から上記のような画面になりますので「新規」ボタンを押してください。
ここでコードがかけるようになります。
Javaではないことは確かなので、Javaのことは忘れてください。

![スクリーンショット 2019-10-02 17.43.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/68de7ba2-8f22-6f7d-0713-0ad2986cf1ab.png)

Simpleが至高だと教えてくれるエディタです。
素直にVSCodeなど他のエディタで書いて上記エディタに貼り付けましょう。

今回はここで行っていますが、VSCodeにはSalesforce用の拡張があってVSCode上でSandboxにクラスを追加できると思います。（たぶん）

## webhookたたく

外部へHttpリクエストを送る処理が含まれている場合 `future` アノテーションが必要になります。

```java:SlackClient.cls
@future(callout=true)
private static void send(String endpoint, String message) {
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint(endpoint);
    request.setMethod('POST');
    request.setHeader('Content-Type', 'application/json');
    request.setBody(message);
    HttpResponse response = http.send(request);
}
```

こんな感じ。

## 詰まったところ

### 親がとれない

たとえば請求を持つ案件の名前を取得したい場合、オブジェクトマネージャでの請求オブジェクトの案件項目は `anken__c` となっているとします。
ただし、子から親の項目を参照する際は `anken__r` になるそうです。

末尾の `__c` が `__r` となっています。

### それでも親がとれない

次の項目のトリガにも関わりますが、トリガ上で対象レコードを取るのに `Trigger` クラスを使います。
`Trigger.new` などが対象のレコードのリストを返すのですが

```java
for (seikyu__c seikyu : Trigger.new) {
    String ankenName = seikyu.anken__r.Name;
} 
```

これで `ankenName` が `null` になります。

```java
for (seikyu__c seikyu : [SELECT anken__r.Name FROM seikyu__c WHERE Id IN :Trigger.new]) {
    String ankenName = seikyu.anken__r.Name;
}
```

こうやって関連項目にアクセスするにはDBから取る必要があるようです。
癖が強い。

# トリガの設定

オブジェクトマネージャ -> 対象のオブジェクト -> トリガから設定できます。

![スクリーンショット 2019-10-02 18.43.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/c8fec9db-4824-92cd-9a6f-ee1213aa25fb.png)

ここで追加しているはそれぞれ削除・追加・更新です。

![スクリーンショット 2019-10-02 18.45.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/d60911e5-8bb3-1ce1-1ef0-024e281f6cb2.png)

ここで請求が追加されると、通知用のメッセージを追加するということを行っています。
エディタの上にある `is_active` のチェックが外れていると思いますが、**新規作成の場合はデフォルトはONで、更新の場合はデフォルトがOFFです。**気をつけましょう。
ここのチェックが外れていると、トリガが実行されません。
Apex トリガから一覧で確認できます。

![スクリーンショット 2019-10-02 18.48.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/e1e4b72a-2fa7-f7bf-d293-7f6a34db21e8.png)

請求のトリガでは、各種変更に合わせてSlackMessageレコードを追加するというトリガになっています。
実際に通知を行っているのはSlackMessageの `after insert` トリガで通知しています。

![スクリーンショット 2019-10-02 18.50.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/0cc9e1d5-4d7a-e7f2-997c-0ccb02e20cb7.png)

# 送信変更セットの作成

クイック検索に `変更セット` と入力すると `送信変更セット` が出てきます。
そこで今回の変更で本番に適用するものを定義します。

![スクリーンショット 2019-10-02 18.52.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/92a2f1c9-3def-0ac5-49f7-31b93d3bf720.png)

こんな感じに作れました。
セキュリティ設定が含まれていませんが、この時は気づいていなかっただけでよく探せばありました。

# リリース設定

本番環境側で、どのsandboxから変更を受け取るか設定します。
これはsandboxを作ったタイミングで実施するもので、同じsandboxを使い回すのであれば基本は設定しなおす必要はないと思います。

![スクリーンショット 2019-10-02 18.53.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/5d4e7f59-ce6a-625c-f848-7ba5d98d8a3e.png)

こんな感じで設定しています。

# 送信変更セットのアップロード

送信変更セットの一覧から対象を選択し `アップロード` を押します。

![スクリーンショット 2019-10-02 19.00.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/b5ac7305-def6-ef58-ab89-794220933a24.png)

アップロードを押せば変更が適用されるはずです。

![スクリーンショット 2019-10-02 19.01.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/6d940ac0-db9b-165f-b535-c06e80cb808e.png)

# 受信変更セットの適用

送信変更セットでアップロードが完了すると受信変更セットからリリースを実行できます。

![スクリーンショット 2019-10-03 14.51.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/3ee1c536-a16f-47aa-6a6f-c08ffb4149cb.png)

リリースからポチポチ進んでいくと…

![スクリーンショット 2019-10-03 14.53.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/56d483cf-3279-0383-9395-c7ddff8fa8f0.png)

おめでとうございます！
ちゃんとテストコードを書きましょう！

実際送信部分はテストコードを書いていましたが、HTTP通信をともなうクラスはテスト不可と出て先に進めませんでした。

どうやら特別な手順が必要なようです。

# コールアウトのMock

`HttpCalloutMock#respond` を実装したクラスをモックとして用います。

```java
@isTest
global class SlackWebhookResponseGenerator implements HttpCalloutMock {

    global HTTPResponse respond(HTTPRequest request) {
        String webhookUrlPrefix = 'https://hooks.slack.com/services';
        HttpResponse response = new HttpResponse();
        response.setHeader('Content-Type', 'application/json');
        response.setStatusCode(200);

        // WebhookのMock
        if (request.getEndpoint().startsWith(webhookUrlPrefix)) {
            response.setBody('{ "ok": true }');
        }

        return response;
    }
}
```

こんな感じで書いています。
これをテストメソッドで使えと宣言します。

```java
Test.setMock(HttpCalloutMock.class, new SlackWebhookResponseGenerator());
```

これはメソッド内で宣言しますので、コールアウト単位でモックを作ることができますが、面倒なので今回は１つで使いまわしています。

# 単体テストの実装

クラスを作成し `@isTest` アノテーションをクラスとメソッドそれぞれに追加します。
テスト中はテスト用のDBを参照しているらしく、データ取得は基本的にテスト中に追加したデータしか参照できません。
これは実際 sandbox で動かしたあと、本番環境でもテストコードが実行されるのでどちらでも動くコードでないと駄目という事だと思います。
いやまあ普通に考えたらそうですよね。

**ただしユーザデータなど、本番とSandboxで微妙に違ったりするのでテストで使うデータはすべてテストコード内で作ったほうが確実です。**

## トリガの単体テストコード

コードカバー率75%以上無いとリリースはできませんが、トリガのコードが0%でもリリースは失敗します。
トリガが発生するケースは必ず網羅する必要があります。

# テストの実行とコードカバー率の確認

Apexテスト実行の「テストを選択」から、アノテーションをつけたクラスを選択できますので、そこから実行できます。

![スクリーンショット 2019-10-03 16.26.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/9255b5ba-39fc-0253-cafc-0ed855fce966.png)

こんな感じで割と見やすいかと思いきやStackTraceがめちゃくちゃ見づらいですが、かわいいもんです。

コードカバー率は Apexクラス の 組織のコードカバー率を見積る リンクから見ることができます。

![スクリーンショット 2019-10-03 16.28.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/961daf06-915f-ca4a-7fed-31f02560404a.png)

ドヤァ…

# リリース！

あとは同じように 本番環境 の 受信変更セット からリリースを選択して見守ります。

![スクリーンショット 2019-10-03 16.13.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36542/9a679692-617c-5970-33d8-771dbadd1c73.png)

無事にSalesforceでの更新情報などをSlackで通知できるようになりました。