読者です 読者をやめる 読者になる 読者になる

dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

follow us in feedly

[Salesforce]Apexコールアウトを利用して、ケース登録時にGitHubのIssueにも登録する

Salesforce apex GitHub

Apexから外部サービスを利用するための方法としてApexコールアウトというのがあります。
今回はこれを使って、GitHubと連携するサンプルを作ってみたという話。
Salesforceでケースが新規作成された時に、指定したGitHubリポジトリにIssueも登録されるというもの。

使用したGitHub APIのバージョンはv3です。

(2014/06/23追記)
すごく基本的なところを勘違いしていたんですが、
HttpRequestで外部サービスを利用するだけならApexコールアウトじゃなくていい んですね。
以下は、トリガから呼ばれる場合など、非同期処理として実行する場合 だと考えてください。
(参考)

リストにカスタムボタンを設置しておいて、押したら外部サービスのREST APIを実行して...とかであれば
普通のクラス内でHttpRequest使えばいいです。

ポイント

  • 連携する外部サービスのURLは「リモートサイトの設定」で設定しておく
  • (追記:トリガの場合は) Apexコールアウトを実装するメソッドには
    @future (callout=true)アノテーションをつけ、メソッドstaticにする

  • @futureアノテーションをつけたメソッドは引数にプリミティブ型(またはプリミティブ型の配列やコレクション)しか渡せない
    SObjectObjectを渡すことができない)

ソースコード

GitHubに公開してあります。

https://github.com/zaki-yama/force_com/tree/master/case2issue

実装してみる

リモートサイトの設定をする

Apexコールアウトでアクセスする外部WebサービスのURLは事前にSalesforce上で設定しておく必要がある。

管理 > セキュリティのコントロール > リモートサイトの設定

から、「新規リモートサイト」を選び、「リモートサイトのURL」に

https://api.github.com

を指定する。
(リモートサイト名は分かりやすい名前をつければOK)

f:id:dackdive:20140609000426p:plain

トリガクラス

before insertトリガを実装する。
トリガ本体にはロジックを書かず、ハンドラで処理する。

CaseTrigger.trigger

trigger CaseTrigger on Case (before insert) {

    if (Trigger.isBefore && Trigger.isInsert) {
        CaseTriggerHandler.handleBeforeInsert(Trigger.new);
    }
}

CaseTriggerHandler.cls

public class CaseTriggerHandler {

    public static void handleBeforeInsert(List<Case> caseList) {
        for (Case c : caseList) {
            System.debug(LoggingLevel.INFO, 'target case:' + c);

            Map<String, String> request = new Map<String, String> {
                'title' => '[' + c.priority + ']' + c.subject,
                'body'  => c.description
            };
            Case2IssueController.createIssue(request);
        }
    }
}

Case2IssueControllerがApexコールアウトを行う今回のメインのクラス。
また、引数にそのままcaseListを渡さずにわざわざMapを作成している理由は後述。

Apexコールアウトを行うクラス

まず、コードがこちら。

public class Case2IssueController {

    private static String USER_NAME = '*****Your GitHub username here*****';
    private static String PASSWORD  = '*****Your GitHub password here*****';
    private static String REPO_NAME = '*****Your GitHub repository name here*****';

    @future (callout=true)
    public static void createIssue(Map<String, String> request) {

        HttpRequest req = new HttpRequest();

        req.setMethod('POST');

        // Set Callout timeout
        // default: 10 secs(that often causes "System.CalloutException: Read timed out")
        req.setTimeout(60000);

        // Set HTTPRequest header properties
        req.setHeader('Connection','keep-alive');
        req.setEndpoint('https://api.github.com/repos/' + USER_NAME + '/' + REPO_NAME + '/issues');
        // Basic Authentification
        Blob headerValue = Blob.valueOf(USER_NAME + ':' + PASSWORD);
        String authorizationHeader = 'Basic ' + EncodingUtil.base64Encode(headerValue);
        req.setHeader('Authorization', authorizationHeader);

        // Set HTTPRequest body
        req.setBody(JSON.serialize(request));

        Http http = new Http();

        try {
            // Execute Http Callout
            HTTPResponse res = http.send(req);

            System.debug(LoggingLevel.INFO, res.toString());
            System.debug(LoggingLevel.INFO, 'STATUS:' + res.getStatus());
            System.debug(LoggingLevel.INFO, 'STATUS_CODE:' + res.getStatusCode());
            System.debug(LoggingLevel.INFO, 'BODY:' + res.getBody());

        } catch(System.CalloutException e) {
            // Exception handling goes here....
            System.debug(LoggingLevel.INFO, e);
        }
    }
}

GitHubの認証はとりあえず簡単なものをということでBasic認証を使ってます。
それ以外の認証方式についてはここを参考に。
GitHub API v3

以下、実装上気をつける点がいくつか。

まず、Apexコールアウトを行うクラスは@future (callout=true)アノテーションが必要。
アノテーションをつけずにトリガハンドラ内で直接実行したりすると、以下のエラーが発生する。

System.CalloutException: Callout from triggers are currently not supported

また、@futureアノテーションをつけたメソッドの引数に List<Case> caseListとかをそのまま渡そうとすると、コンパイルエラーになります。

classes/Case2IssueController.cls -- Error: Unsupported parameter type LIST<Case>

これについては

Future Annotation

ここを見ると、次のように書いてある。

The specified parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types. Methods with the future annotation cannot take sObjects or objects as arguments.

つまり、プリミティブ型またはその配列やコレクションしか認めていないということですね。

試してみる

Salesforceからケースを作成してみる。

f:id:dackdive:20140609003515p:plain

さて、GitHubの指定したリポジトリを見てみると...

f:id:dackdive:20140609003533p:plain

登録されてます!
というわけで、実装方法で注意しないといけない部分はあるものの、比較的簡単に外部サービスと連携できました。

リファレンス

HttpRequestクラス HttpRequest Class

HttpResponseクラス HttpResponse Class

Caseクラス Case

GitHub API v3 Issues | GitHub API

公式ドキュメントなんだけど、あまり情報が載ってない
JP:Apex Web Services and Callouts - developer.force.com

以下は技術ブログ
deferloader » [salesforce]トリガ内からのHTTPコールアウト実行

[SFDC]Apexトリガから外部サービスを呼び出す | 遊び場