7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SnowRobinAdvent Calendar 2019

Day 9

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

Posted at

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

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

基本的にここを参考にしています。

sandbox環境を作成

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

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

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

スクリーンショット 2019-10-02 17.32.24.png

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

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

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

スクリーンショット 2019-10-02 17.36.00.png

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

セキュリティ設定

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

スクリーンショット 2019-10-02 18.58.00.png

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

Slack送信部分の実装

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

スクリーンショット 2019-10-02 17.41.31.png

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

スクリーンショット 2019-10-02 17.43.43.png

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

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

webhookたたく

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

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 などが対象のレコードのリストを返すのですが

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

これで ankenNamenull になります。

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

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

スクリーンショット 2019-10-02 18.45.18.png

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

スクリーンショット 2019-10-02 18.48.20.png

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

スクリーンショット 2019-10-02 18.50.00.png

送信変更セットの作成

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

スクリーンショット 2019-10-02 18.52.48.png

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

リリース設定

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

スクリーンショット 2019-10-02 18.53.47.png

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

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

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

スクリーンショット 2019-10-02 19.00.11.png

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

スクリーンショット 2019-10-02 19.01.14.png

受信変更セットの適用

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

スクリーンショット 2019-10-03 14.51.48.png

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

スクリーンショット 2019-10-03 14.53.26.png

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

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

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

コールアウトのMock

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

@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;
    }
}

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

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

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

単体テストの実装

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

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

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

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

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

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

スクリーンショット 2019-10-03 16.26.44.png

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

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

スクリーンショット 2019-10-03 16.28.56.png

ドヤァ…

リリース!

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

スクリーンショット 2019-10-03 16.13.16.png

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

7
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?