ApexでSalesforceとBox連携した話

# はじめに
この記事は、Apexを使ったBox連携の話になります。
参考になるかどうかわからないですが、Apexを使ったBox連携の文献が少なかったので残します。
（私の初投稿記事となります。）

# SalesforceとBoxの連携
appexchange（Salesforce store）より、Box for Salesforceをインストールします。
こちらをインストールすると少しはApexでの連携が楽になります。

・Box for Salesforce
https://appexchangejp.salesforce.com/appxListingDetail?listingId=a0N3000000B4FmeEAF

・インストール方法
https://support.box.com/hc/ja/articles/360044195713-Box-for-Salesforce%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

# Box連携でやったこと
ある帳票をメールで送信するために、添付ファイル形式から、Boxの共有リンクへ変更しました。
Boxの共有リンクを作るために、下記のような流れで処理を行っています。

①ファイル（Attachment）の作成
②ファイル（Attachment）をBoxへアップロード
③Boxへアップロードしたファイルの共有リンクの生成

# ①ファイル（Attachment）の作成
こちらは、いろいろな方法がありますが、今回はサイト経由でVisualforceを呼び出し、ファイル（Attachment）を生成しています。（サイト経由なのは、CSRFの対策のためです。）
なぜ、ファイル（Attachment）を生成するかというと、box Toolkitを使ってBoxへファイルアップロードをするためとなります。

※box ToolkitとはBox for Salesforceに組み込まれているSalesforce Developer Toolkitとなります。

・box Toolkit
https://ja.developer.box.com/guides/tooling/salesforce-toolkit/

（参考ソース）
```apex:getAttachmentクラス
    global static List<Attachment> getAttachment(List<object> objList) {
        List<Attachment> attList = new List<Attachment>();
        String ym = String.valueOf(Datetime.now().format('yyyyMM','JST'));
        
        //サイト情報を取得 
        Site site = [SELECT Id from Site where Name = 'test site' Limit 1];
        SiteDetail sd = [SELECT Id, DurableId, IsRegistrationEnabled, SecureUrl FROM SiteDetail where DurableId = :site.Id];
        String url = sd.SecureUrl + '/apex/TestForms';
        
        for (object obj : objList) {
            PageReference pageRef = new PageReference(url);            
            pageRef.getParameters().put('id', afItem.Id);            
            Attachment att = new Attachment();
            att.Name = String.format('【テスト】{0}_{1}.pdf', new List<Object>{obj.Name, ym});
            //Apexテスト時のエラー回避
            if (Test.isRunningTest() == false) {
                //通常時
                att.Body = pageRef.getContent();
            }else{
                //テスト時
                att.Body = Blob.valueOf('TEST_abc');
            }
            att.ParentId = obj.Id;
            attList.add(att);
        }

        return attList;
    }

```

# ②ファイル（Attachment）をBoxへアップロード<br>③Boxへアップロードしたファイルの共有リンクの生成
box Toolkitを使ってアップロード処理と、共有リンクを生成しています。
共有リンクについては、box Toolkitに準備されていなかったので、HttpRequestで直接APIをコールしています。

・box APIリファレンス
https://ja.developer.box.com/reference/

・box Toolkit：createFileFromAttachment（AttachmentをBoxへアップロードする）
https://ja.developer.box.com/guides/tooling/salesforce-toolkit/methods/#createfilefromattachment

・box Toolkit：sendRequest（Salesforceで設定したユーザで認証するので認証を省略できる）
https://ja.developer.box.com/guides/tooling/salesforce-toolkit/methods/#sendrequest

（参考ソース）
```apex:getBoxSharedLinkクラス
    global static List<String> getBoxSharedLink(Id objID, Integer sharedDays, String pw) {
        //戻り値（[0]true:成功、false:失敗 [1]BoxファイルID [2]共有リンク）
        List<String> boxFileID_URL = new List<String>();
        String boxFileID = null;
        String url = null;
        
        //Objectからファイル（Attachment）を取得する。
        List<Attachment> attList = [SELECT Id, Name, Body, ParentId FROM Attachment WHERE ParentId = :objID];
                
        //box Toolkit
        box.Toolkit toolkit = new box.Toolkit();
      
        //Boxアップロード処理
        try{
            //boxへアップロード（box Toolkitでアップロード）
            boxFileID = toolkit.createFileFromAttachment(attList[0], null, null, null);            
        }catch(Exception e){
            system.debug('■■■ファイルID：' + boxFileID);
            system.debug('■■■エラー内容：' + toolkit.mostRecentError);
            boxFileID_URL.add('false');
            boxFileID_URL.add('Boxへのアップロード失敗：');
            boxFileID_URL.add(toolkit.mostRecentError);
            return boxFileID_URL;
        }


        //リクエスト（共有リンク追加）
        Date unshared_date = Date.today() + sharedDays;
        String unshared = String.valueOf(unshared_date) + 'T00:00:00+09:00';        
        String endpoint = 'https://api.box.com/2.0/files/' + boxFileID;
        String body = '{"shared_link":{"access": "open", "password":"' +pw+ '", "unshared_at":"' +unshared+ '"}}';
        HttpRequest request = new HttpRequest();
        request.setMethod('PUT');
        request.setEndpoint(endpoint);
        request.setHeader('content-type', 'application/json');
        request.setBody(body);

        //共有リンク作成処理
        HttpResponse response;
        try{
            //認証を省くため、toolkitからリクエストする
            response = toolkit.sendRequest(request);
            system.debug('■■■res：'+response);
            system.debug('■■■res_body：'+response.getBody());
            
            Map<String, Object> mapResponseData = (Map<String, Object>)Json.deserializeUntyped(response.getBody());
            Map<String, Object> dataToShardeLink = (Map<String, Object>)mapResponseData.get('shared_link');
            
            //ステータスチェック
            if(response.getStatusCode() == 200){
                url = dataToShardeLink.get('url').toString();
            }else{
                boxFileID_URL.add('false');
                boxFileID_URL.add('共有リンク作成失敗（ステータス200以外）：');
                boxFileID_URL.add(response.getBody());
                return boxFileID_URL;
            }            
        }catch(Exception e){
            boxFileID_URL.add('false');
            boxFileID_URL.add('共有リンク作成失敗（catch）：');
            if(response != null){
                boxFileID_URL.add(response.getBody());
            }
            system.debug('■■■（catch）ファイルID：' + boxFileID);
            system.debug('■■■（catch）エラー内容：' + toolkit.mostRecentError);
            return boxFileID_URL;
        }
        
        boxFileID_URL.add('true');
        boxFileID_URL.add(boxFileID);
        boxFileID_URL.add(url);
        return boxFileID_URL;
    }
```

# おわりに
今回、Apexを用いてBox連携を行いました。
Box for Salesforceが提供する、box Toolkitがもう少し充実していたらもっと楽に実装できたかと感じました。
Apexでなければ、Javaや.NET、PythonなどのSDKは準備されているのでApexを使って連携するケースは少ないのかと想定されてます。今後も、ニッチな連携を行ったら記事に残したいと思うので、よろしくお願いします。