Amazon API Gateway を Salesforce からコールアウトする

Amazon API Gatewayで作成したAPIをSalesforceのコールアウトで呼び出す方法について。Apexの(コールアウト処理の)非同期呼び出しの例として、Queueableインタフェースの実装例にもなっています。
2021.03.05

Amazon API Gateway を使って作成した Rest API を Salesforce からコールアウトしてみたいと思います。

Salesforce からのコールアウトのイメージを掴むには下記のエントリーもご参照ください。

[Salesforce] リード作成時にコールアウトを使ってJiraにチケットを作る

Amazon API Gateway で Rest APIを作る

API GatewayとStep Functionsを組み合わせた非同期APIが最強だった話

の記事のサンプルをベースに使わせていただきました。

サンプルではoutputが固定なので、少し改修してinputの値を表示するようにしました。

diff --git a/app.py b/app.py
index 9f20415..ac0c7fb 100644
--- a/app.py
+++ b/app.py
@@ -12,4 +12,4 @@ def lambda_handler(event, context):
     seconds = int(os.environ.get("TIMER"))
     sleep(seconds)

-    return {"message": "らーめんが ゆであがりました はやく たべないと のびて しまいます"}
+    return {"message": f"{event['lead-name']}さんに用意した{event['aji']}らーめんが ゆであがりました はやく たべないと のびて しまいます"}

実行は次の通りです。

デプロイして1

$ sls deploy --stage default

Serverless Frameworkデプロイ

エンドポイントを設定して、

# Set Api Endpoint
$ StartExecution=https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/<ステージ名>/ramen/start-execution
$ DescribeExecution=https://XXXXXXXXXX.execute-api.ap-northeast-1.amazonaws.com/<ステージ名>/ramen/describe-execution

実行する。

# StartExecution
$ executionArn=$(curl -X POST -d '{"lead-name": "進地", "aji": "みそ"}' $StartExecution | jq -r '.executionArn')

# DescribeExecution
$ curl -X POST -d '{"executionArn": "'$executionArn'"}' $DescribeExecution | jq

結果は、DescribeExecutionStartExecutionの実行直後の実行だと、

% curl -X POST -d '{"executionArn": "'$executionArn'"}' $DescribeExecution | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   208  100    77  100   131    350    595 --:--:-- --:--:-- --:--:--   945
{
  "input": {
    "lead-name": "進地",
    "aji": "みそ"
  },
  "status": "RUNNING"
}

しばらく待ってから再実行すると、

$ curl -X POST -d '{"executionArn": "'$executionArn'"}' $DescribeExecution | jq
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   488  100   357  100   131    642    235 --:--:-- --:--:-- --:--:--   877
{
  "input": {
    "lead-name": "進地",
    "aji": "みそ"
  },
  "output": {
    "message": "進地さんに用意したみそらーめんが ゆであがりました はやく たべないと のびて しまいます"
  },
  "status": "SUCCEEDED"
}

となります。

Salesforce から Amazon API Gateway で作った Rest API をコールアウトする

それでは、先に作成したAPIをSalesforceからコールアウトしてみます。

指定ログイン情報の作成

まず、Amazon API GatewayをApexからコールアウトできるように、指定ログイン情報を作成します。

Amazon_API_Gateway用 指定ログイン情報

指定ログイン情報に設定する各値は次の通りです。

項目 設定値
表示ラベル AWS_APIGW
名前 AWS_APIGW
URL https://<発行されたAmazon API Gatewayのホスト名>.execute-api.<お使いのリージョン(ap-northeast-1など)>.amazonaws.com
ID 種別 指定ユーザ
認証プロトコル AWS 署名バージョン 4
AWS アクセスキー ID2 AWSの接続に利用するアカウント/ロールのアクセスキーID
AWS シークレットアクセスキー2 AWSの接続に利用するアカウント/ロールのシークレットアクセスキー
AWS リージョン2 お使いのリージョン(ap-northeast-1など)
AWS サービス2 apigateway
他の項目 入力、チェックしない

AWSに対して接続する場合は認証プロトコルに「AWS 署名バージョン 4」が用意されていますので、認証プロバイダを自前で設定する必要がなくて楽ちんです。

コールアウトの実装

それでは、SalesforceでAmazon API Gateway をコールアウトする処理を実装します。

public with sharing class APIGatewayCalloutAsync implements Queueable,Database.AllowsCallouts {
    private final String body;
    private final Integer period;

    public APIGatewayCalloutAsync(String body, Integer period) {
        this.body = body;
        this.period = period;
    }

    public void execute(QueueableContext context) {
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:AWS_APIGW/default/ramen/describe-execution');
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Accept', 'application/json');
        req.setBody(this.body);
        HttpResponse res = http.send(req);
        CalloutResponse cres = (CalloutResponse)JSON.deserializeStrict(res.getBody(), CalloutResponse.class);
        if ( cres.status != 'SUCCEEDED' ) {
            Long startTime = DateTime.now().getTime();
            Long finishTime = DateTime.now().getTime();
            while ((finishTime - startTime) < (this.period * 1000)) {
                //sleep for this.period sec
                finishTime = DateTime.now().getTime();
            }
            System.enqueueJob(this);
        } else {
            System.debug(cres.output.get('message'));
        }
    }

    class CalloutResponse {
        Map<String,String> input;
        Map<String,String> output;
        String status;
    }
}

APIGatewayCalloutAsync は非同期にAWS_APIGW/default/ramen/describe-executionへのコールアウトを繰り返すQueueableを実装したクラスです。コンストラクタの第二引数で与えた秒数間隔をあけて第一引数で与えたJSONをパラメータに使ってコールアウトします。

コールアウトから{"status": "SUCCEEDED"}が返ってくるまで繰り返し、返ってきたら{"output": {"message": "メッセージ"}}をデバッグ出力して終了します。

呼び出し側は、例えば開発者コンソールの[Debug] > [Open Execute Anonymous Window] にて次のコードを実行します。

HttpRequest req = new HttpRequest();
req.setEndpoint('callout:AWS_APIGW/default/ramen/start-execution');
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setHeader('Accept', 'application/json');
req.setBody('{"lead-name": "進地", "aji": "みそ"}');

Http http = new Http();
HttpResponse res = http.send(req);

ExecutionArn arn = (ExecutionArn)JSON.deserializeStrict(res.getBody(), ExecutionArn.class);
System.enqueueJob(new APIGatewayCalloutAsync('{"executionArn": "' + arn.executionArn + '"}', 10));

class ExecutionArn {
    String executionArn;
    String startDate;
}

AWS_APIGW/default/ramen/start-executionを呼び出してExecutionArnを取得し、取得したExecutionArnと繰り返しの間隔に10秒を指定してAPIGatewayCalloutAsyncオブジェクトをキューに追加しています。キューは10秒間隔でAPIGatewayCalloutAsync.execute()を実行し、先述の通りにoutputが返ってきたら終了します。

このサンプルの場合、最終結果として、進地さんに用意したみそらーめんが ゆであがりました はやく たべないと のびて しまいますという文章がデバッグ出力されれば成功です。

Amazon API Gatewayのコールアウト結果

まとめ

Amazon API Gateway で作成した Rest API エンドポイントに対して、Salesforceからコールアウトする方法について書きました。 コールアウトでJiraにチケットを作る場合と比べても圧倒的に設定がシンプルで楽であることがおわかりいただけたかと思います。

また、 Amazon API Gateway と Step Functions で構成された非同期APIの実行処理に対して、処理完了したか判断してレスポンスを受け取るためのAPI(AWS_APIGW/default/ramen/describe-execution)を非同期で繰り返し実行する方法としてApexのQueueableインターフェースを実装する例を書いてみました。

AWS と Salesforce のインテグレーションの一形式として活用を検討してみてください。

参考資料


  1. --stageに指定する環境は適宜変えてください。 
  2. 更新画面だけに入力UIが現れます。