はじまる

適当な事を適当に書く

Salesforce の Apex Class から 外部のWebAPIを叩く。

触るまいとしていたSalesforceを触らないといけない感じに。

やりたいこと

実装方針

  • レコード詳細ページのカスタムボタンを押したら、ある項目の値をHeroku にあるサイトのサブドメインとして設定する。
  • カスタムボタンからjavascriptを実行、Apex Class を呼び出す感じで。
  • Salesforce の Apex Class から 外部のWebAPIを叩く。
  • BASIC認証などで user、password など使う場合は、「カスタム設定」というところに隠し、それを使うようにする。

実装手順

  1. リモートサイトの設定
  2. カスタム設定
  3. Apex Class 記述
  4. カスタムボタンまたはカスタムリンク
  5. Javascriptを実行
  6. ページレイアウト編集
  7. テスト

やってみた

リモートサイトの設定

設定クイック検索ボックスで「リモート」を検索>リモートサイトの設定新規リモートサイト でエンドポイントのURLを登録しておく。(しないとSalesforceとエンドポイントの間でHTTPリクエストレスポンスのやりとりができない) f:id:satorusangakoronda:20160429185631p:plain

カスタム設定

秘匿したい情報はカスタム設定のカスタム項目に保存できる。設定クイック検索ボックスで「カスタム設定」を検索。外部WebAPIの認証に必要なAPIキーなどはここに格納し、Apex には直接APIキーなどは記述せず、カスタム項目に格納された値を参照するように記述する。

f:id:satorusangakoronda:20160429190307p:plain

f:id:satorusangakoronda:20160429190320p:plain

ちなみに、カスタム設定は Apex のなかではこんな感じで呼び出すらしい。

class SomeClass{
    public static String someMethod(){
        CustomSetting cs = CustomSetting();
        String a = cs.column_a;
    } 

    private class CustomSetting{
       my_custom_setting__c cs = my_custom_setting__c.getInstance('user_id'); //保存先をユーザにした場合

       String column_a = column_a__c;
       String column_b = column_b__c;
       String column_c = column_c__c;
     }
}

class のなかに定義するより、外部に独自にclass定義して、他のclassからはそれを呼び出すようにする方がいいかしら。

public class Secret {
    public string heroku_api_key;
    public string dns_account;
    public string dns_zone_id;
    public string dns_api_key;
    
    public Secret() {
        Credential cr = new Credential();
        this.heroku_api_key = cr.heroku_api_key;
        this.dns_account = cr.dns_account;
        this.dns_zone_id = cr.dns_zone_id;
        this.dns_api_key = cr.dns_api_key;
    }
        
    private class Credential{
        String user_id = 'hoge';
        my_custom_setting__c mc = my_custom_setting__c.getInstance(user_id); 
        String heroku_api_key = mc.heroku_api_key__c;
        String dns_account = mc.dns_account__c;
        String dns_api_key = mc.dns_api_key__c;
        String dns_zone_id = mc.dns_zone_id__c;
    }
}
Apex Class 記述

とりあえず、こんな感じでかけばいいらしい(※正常処理のみ)。下記のEndpointはテストサイト(httpbin.org)だけど、実際にはここをHeroku Platform API にする。

global class httpRequestDemo {
    webService static String makeGetCallout() {

        String result = 'hoge';
        
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        Blob credential = Blob.valueOf('username:password');

        request.setEndpoint('https://httpbin.org/get');
        request.setMethod('GET');
        request.setHeader('Accept', 'application/json');
        request.setHeader('Content-type', 'application/json');
        request.setHeader('Authorization',  'BASIC ' + EncodingUtil.base64Encode(credential));

        HttpResponse response = http.send(request);
        Integer status = response.getStatusCode();
        if (status == 200){
            result = response.getBody();
        }else{
            result = status.format() + response.getBody();
        }
        return result;
    }
}

追記;こんな感じでカスタムドメインの設定できた。コードが良くなさそうだけど、Apexがよくわからないので、どう改善したらいいものか思案中。

global class HerokuAPI {
    
    webService static String createNewDomain() {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        Credential conf = new Credential();

        request.setEndpoint('https://api.heroku.com/apps/app_name/domains');
        request.setHeader('Authorization', 'Bearer ' + conf.heroku_api_key);
        request.setHeader('Accept', 'application/vnd.heroku+json; version=3');
        request.setHeader('Content-type', 'application/json');
        request.setMethod('POST');
        request.setBody('{"hostname": "subdomain.example.com"}');

        HttpResponse response = http.send(request);
        Integer status = response.getStatusCode();

        if (status == 200){
            return '200:' + response.getBody();
        }else{
            return status.format() + response.getBody();
        }
    }
        
    private class Credential{
        String user_id = 'ユーザーのオブジェクトID';
        credential___c cr = credential___c.getInstance(user_id); 
        String heroku_api_key = cr.heroku_api_key__c;
    }
    
}
カスタムボタンまたはカスタムリンク

カスタムボタンまたはカスタムリンク の「動作」で「Javascriptを実行」を選択し、下記のようなjsを書く。

{!REQUIRESCRIPT('/soap/ajax/36.0/connection.js')} 
{!REQUIRESCRIPT('/soap/ajax/36.0/apex.js')} 
var ret = sforce.apex.execute('HttpBinInterface','makeGetCallout',{}); 
alert(ret);

参考