4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ApexでAccount Engagementの行動履歴を取得する

Last updated at Posted at 2024-09-03

概要

ApexでAccount Engagementの行動履歴をAPIで取得する処理を作成したときの備忘です。
API実行に必要なアクセストークンを取得する認証APIとAccount Engagementの行動履歴を取得するAPIの2コを利用します。
認証APIにはいくつか方式がありますが、ユーザー名パスワードフローを使用しました。ユーザ名とパスワードを送信するため、非推奨とされていますが、クライアントログイン情報フローを使うとコールバックURLにリダイレクトしてしまうため、おなじApex内で処理が続行できませんでした。ユーザー名パスワードフローはリダイレクトしなかったのですが、クライアントログイン情報フローでリダイレクトしないやり方はないのですかね。ご存じの方、いらっしゃいましたら、教えてください。
2024年8月時点の情報になります。

Account Engagementの行動履歴とは

プロスペクトがメールを受信、開封したり、ランディングページにアクセスしたりといったプロスペクトのアクティビティに表示される情報です。この情報はSales Cloudからオブジェクトとして参照できないので、APIで取得します。
アクティビティ.png

設定

アプリケーションマネージャー

「アプリケーションマネージャー」で「新規接続アプリケーション」を作成します。
アプリケーションマネージャー1.png

「OAuth 設定の有効」のチェックを「オン」に変更し、「利用可能な OAuth 範囲」を選択します。今回は、Account Engagement APIを利用したいので、「Pardot サービスを管理 (pardot_api)」を選択します。
アプリケーションマネージャー2.png

接続アプリケーションを作成すると「コンシューマー鍵」と「コンシューマーの秘密」が発行されます。認証APIで必要になるので、覚えておきます。
アプリケーションマネージャー3.png

接続元のIPアドレスの制限として「IP制限の緩和」という項目があり、値を「IP制限の緩和」としました。今回はApexから呼び出すので、厳格にやる場合は、「IP制限を適用」とし、あとで作成する接続用ユーザのプロファイルのIPアドレス制限にSalesforceのIPアドレスをすべて設定します。
ip制限の緩和.png

リモートサイトの設定

「リモートサイトの設定」でSalesforceから外部接続を許可するURLを追加します。
リモートサイトの設定.png

※本番環境とテスト環境でURLが異なるので注意してください

production

sandbox

OAuth および OpenID Connect 設定

「OAuth および OpenID Connect 設定」で「OAuthユーザー名パスワードフローを許可」を「オン」に変更します。
OAuth および OpenID Connect 設定.png

ユーザー

「ユーザー」でAPI実行用ユーザーを作成します。ユーザーライセンスは「Identity」プロファイルは「Identity User」を選択します。
ユーザー1.png

ユーザーを作成したら、権限セットの割り当てで「Account Engagement Integration User」を追加します。Account Engagementの情報を参照するのに必要な権限になります。
ユーザー2.png

認証API

SalesforceのAPIを実行するためのアクセストークンを取得するAPI

マニュアル

特別なシナリオの OAuth 2.0 ユーザー名パスワードフロー

リクエスト

エンドポイント

環境 エンドポイント メソッド
production https://login.salesforce.com/services/oauth2/token POST
sandbox https://test.salesforce.com/services/oauth2/token POST

パラメータ

キー
grant_type password
client_id 接続アプリケーションで発行した「コンシューマー鍵」
client_secret 接続アプリケーションで発行した「コンシューマーの秘密」
username API実行用に作成したユーザの「ユーザ名」
password API実行用に作成したユーザの「パスワード」

ヘッダー
不要

レスポンス

キー
access_token APIで利用するアクセストークン
instance_url 使わない
id 使わない
token_type 使わない
issued_at 使わない
signature 使わない

Account Engagement Visitor Activity API

Account Engagementの行動履歴を取得するAPI

マニュアル

Visitor Activity

リクエスト

エンドポイント

環境 エンドポイント メソッド
production https://pi.pardot.com/api/v5/objects/visitor-activities GET
sandbox https://pi.demo.pardot.com/api/v5/objects/visitor-activities GET

パラメータ(詳細はマニュアルをみてください)

キー
fields 取得する項目をカンマ区切りで列挙(マニュアルをみてください)
limit データの取得件数(デフォルト200で、1,000まで指定可)
ソート項目 ソート条件(マニュアルをみてください)
フィルタ項目 フィルタ条件(マニュアルをみてください)

ヘッダー

キー
Authorization Bearer 認証APIで取得したアクセストークン
Pardot-Business-Unit-Id ビジネスユニットID

※Authorizationの値のBearerのあとは半角スペースがはいります。
※ビジネスユニットIDは「ビジネスユニット設定」で確認してください。
ビジネスユニット設定.png

レスポンス

キー
nextPageToken 次のページアクセストークン(なければnull)
nextPageUrl 次のページのURL(なければnull)
values 取得した値

※1回のリクエストで最大1,000件までのデータしか取得できません。すべてのデータが取得できない場合、「nextPageToken」と「nextPageUrl」に値が設定され、「nextPageUrl」をたどっていくことですべてのデータを取得することができます。「nextPageUrl」にリクエストする際、「nextPageToken」でなく、最初に認証APIで取得したアクセストークンでも動作しました。

Apex

実装

API呼び出しのサンプルプログラムです。

  • フローからApexアクションで呼び出すのにInvocableMethodアノテーションを使用しています
  • APIを呼び出すには、futureアノテーションを使用しないと、CalloutExceptionが発生します
  • データを複数件取得する場合、レスポンスの「nextPageUrl」を判定して、nullになるまで、apiをよぶ必要があります(サンプルプログラムではやっていません)
  • APIのエンドポイントなど、環境依存の定数はカスタム表示ラベルを使用しています
apiSample.apxc
public class ApiSample {
    // 定数
    // 認証API URL
    static final String AUTHENTICATION_API_URL = System.Label.Authentication_Api_Url;
    // VISITOR ACTIVITY API URL
    static final String VISITOR_ACTIVITY_API_URL = System.Label.Visitor_Activity_Api_Url;
    // コンシューマ鍵
    static final String CLIENT_ID = System.Label.Client_Id;
    // コンシューマ秘密
    static final String CLIENT_SECRET = System.Label.Client_Secret;
    // ユーザ名
    static final String USERNAME = System.Label.Username;
    // ユーザパスワード
    static final String PASSWORD = System.Label.Password;
    // ビジネスユニットID
    static final String BUSINESS_UNIT_ID = System.Label.Business_Unit_Id;

    /**
     * フローから呼び出されるメソッド
     * @param batchParams パラメータ
     * @return なし
     * 
     **/
    @InvocableMethod(label='Run Batch Job')
    public static void runBatchJob(List<String> batchParams) {
		main();
    }
    
    /**
     * メイン処理
     * 
     * @return なし
     * 
     **/
    @future(callout=true)
    public static void main() {
        // 認証APIをよぶ
        String accessToken = apiAuthentication();

        // visitor activity apiをよぶ
        apiVisitorActivity(accessToken);        
    }
    
    /**
     * visitor activity apiを実行する
     * 
     * @param accessToken アクセストークン
     * @return apiレスポンスのbody
     * 
     **/
    private static String apiVisitorActivity(String accessToken) {
		// httpリクエストとレスポンスの初期化
        HttpRequest req = new HttpRequest();
        HttpResponse res = null;
		Http http = new Http();
        
        // getパラメータの設定
        String param = '?fields=prospect.email';
        param += '&type=6';
        param += '&limit=1000';
        param += '&orderby=createdAt';
        
        // リクエストの設定
        req.setEndpoint(VISITOR_ACTIVITY_API_URL + param);
        req.setMethod('GET');
        
        // リクエストヘッダの設定
        req.setHeader('Authorization', 'Bearer ' + accessToken);
        req.setHeader('Pardot-Business-Unit-Id', BUSINESS_UNIT_ID);
                
        try {
            // httpリクエスト送信
            res = http.send(req);
            
            // ステータスコードのチェック
            if (res.getStatusCode() != 200) {
                System.debug('api call failure:' + res.getBody());
                return null;
            }
        } catch (Exception e) {
            System.debug('Exception:' + e.getMessage());
        }
        
        return res.getBody();
    }
    
    /**
     * 認証apiを実行する
     * 
     * @param accessToken アクセストークン
     * @return apiレスポンスのbody
     * 
     **/
    private static String apiAuthentication() {       
		// httpリクエストとレスポンスの初期化
        HttpRequest req = new HttpRequest();
        HttpResponse res = null;
		Http http = new Http();
        
        // リクエストの設定
        req.setEndpoint(AUTHENTICATION_API_URL);
        req.setMethod('POST');
        
        // リクエストヘッダの設定
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        
        // リクエストボディの設定
        String body = 'grant_type=' + EncodingUtil.urlDecode('password', 'utf-8');
        body += '&client_id=' + EncodingUtil.urlDecode(CLIENT_ID, 'utf-8');
        body += '&client_secret=' + EncodingUtil.urlDecode(CLIENT_SECRET, 'utf-8');
        body += '&username=' + EncodingUtil.urlDecode(USERNAME, 'utf-8');
        body += '&password=' + EncodingUtil.urlDecode(PASSWORD, 'utf-8');
        req.setBody(body);

        try {
            // httpリクエスト送信
            res = http.send(req);
            
            // ステータスコードのチェック
            if (res.getStatusCode() != 200) {
                System.debug('api call failure:' + res.getBody());
                return null;
            }
        } catch (Exception e) {
            System.debug('Exception:' + e.getMessage());
        }
        
        // アクセストークンを返却
        Map<String, Object> jsonResponse = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
        return (String)jsonResponse.get('access_token');
    }
}

テストクラス

テストクラスのサンプルプログラムです。

  • API呼び出しはモックを使用します
  • HttpResponseのレスポンス処理がすべて同一のモック処理になってしまうので、リクエストURLを判定することで、それぞれのリクエストに応じたレスポンスを返すようにしています
apiSampleTest.apxc
@isTest
private class ApiSampleTest {
	@isTest
    private static void test1() {
        Test.setMock(HttpCalloutMock.class, new CalloutMock());
        
        Test.startTest();
        ApiSample.runBatchJob(null);
        Test.stopTest();
    }
        
    // apiモック
    private class CalloutMock implements HttpCalloutMock {
        public HttpResponse respond(HttpRequest req) {
            HttpResponse res = new HttpResponse();
            
            // 認証apiのレスポンス
            if (req.getEndpoint().contains(System.Label.Authentication_Api_Url)) {
				res.setStatusCode(200);
                res.setBody('{"access_token": "xxx","instance_url": "xxx","id": "xxx","token_type": "xxx","issued_at": "xxx","signature": "xxx"}');
            }
            // visitor activity api1回目のレスポンス
            if (req.getEndpoint().contains(System.Label.Visitor_Activity_Api_Url)) {
                    res.setStatusCode(200);
                    res.setBody('{"nextPageToken": "","nextPageUrl": "https://nextpageurl","values":[]}');
            }
            // visitor activity api2回目のレスポンス
            if (req.getEndpoint().contains('https://nextpageurl')) {
                    res.setStatusCode(200);
                    res.setBody('{"nextPageToken": null,"nextPageUrl": null,"values": []}');
            }

            return res;
        }
    }
}

エラー調査

認証がうまくいかない場合、「設定」「ユーザー」から、API実行用ユーザのログイン履歴をみると、失敗した原因が確認できるので、参考になりました。

4
0
5

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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?