Salesforce にAPI モックを作る


外部システムと連携する機能の実装は、Salesforce に限らずよくありますが、連携先の Web API が利用できない、または利用できても自由が効かない場面に遭遇したことがある方は少なくないでしょう。このとき、一般の Web 開発であれば、例えば [Mockoon](https://mockoon.com/) のようなツールを使ってローカルにサーバを立てて開発を進めたりといった手段が取れます。しかし、Salesforce のようにローカルでの開発・テストが難しい場合、同じ方法は取れません。ローカルではなく Web 上でいい感じに固定値を返してくれる API が簡単に用意できれば、フローや Apex からのコールアウトのようなシステム連携を含む Salesforce プラットフォーム上の開発をスムーズに進められそうです。

この記事では、Salesforce 上に Apex REST を用いてAPI モックを作る方法を紹介します。既存で類似の Web サービスに例えば [Mocky](https://designer.mocky.io/) などがありますが、今回の方法は、レスポンスを静的リソース内の JSON ファイルとして管理するなど、Salesforce 開発者が慣れ親しんでいるであろう方法で易しく設定・管理できることを目的にしています。

## ソースコード
https://github.com/shunkosa/apex-mock-web-service

## 機能の構成
![アーキテクチャ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/e3e0ccb9-db3e-421a-bf93-055930feedd4.png)


## 設定手順
1. 上記リポジトリの `mock-web-service` フォルダのメタデータをデプロイします 
    ```
    sf project deploy start --source-dir mock-web-service
    ```

2. モック API レスポンスとなる、任意の JSON ファイル群を zip にして任意の名前で静的リソースに保存します
3. カスタムメタデータ `Mock Web Service Setting` にレコードを追加します。このカスタムメタデータでは、リクエストのパスに対応するレスポンスの静的リソース・JSON ファイルと、ステータスコードを設定することができます。リクエスト URI項目は、[Apex REST](https://github.com/shunkosa/apex-mock-web-service/blob/main/mock-web-service/main/default/classes/MockWebService.cls#L4) で定義しているパスである `/mock` から始めてください
![カスタムメタデータの例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/6eb40fbe-b8a9-3ed7-43ba-657dbed744e4.png) 例えば上記のように設定した場合、`https://yourdomain.my.salesforce.com/services/apexrest/mock/user/0001` は、静的リソース `MockApiResponse` 内の `example01.json` の内容を返却するようになります。

zip 化された静的リソースは、ローカルの Salesforce DX プロジェクトでは通常のフォルダとファイルとして扱えます。レスポンスを直接変更したい場合は、コードと同様にローカルでファイルを修正してデプロイすると良いでしょう。

![VS Code](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/706d5ae7-02f4-dabb-8946-3fb0c6991f1a.png)

## 指定ログイン情報を用いて Salesforce から API モックをコールする
これまでの設定で Salesforce 上にAPI モックを用意することができました。続いて、Apex からこれにコールアウトしてみましょう。リポジトリにはサンプルのアプリケーションも含めていますので、それをデプロイして試していただくこともできます。また、API モックとそれをコールアウトする機能は同一組織に置くこともできます。

1. 認証プロバイダを作成します。プロバイダタイプは `Salesforce` に、コンシューマー鍵とコンシューマの秘密は、先の手順でデプロイした資源に外部クライアントアプリケーション `Apex Mock Web Service` があるので、このコンシューマー鍵とコンシューマーの秘密の値を設定してください。保存後、表示されるコールバックURL を、外部アプリケーションのコールバック URL に設定します
![認証プロバイダの設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/660992f7-776b-444c-8b90-862fc008ac8d.png)
2. 外部ログイン情報を作成します。指定ログイン情報の画面から新しく外部ログイン情報を作成します。認証プロトコルは `OAuth 2.0`、認証フローは`ブラウザフロー`、認証プロバイダで直前に作成した認証プロバイダを選択します。scope には `api web refresh_token` を入れておきましょう。
![External Credential](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/617d4fef-1ef2-4d6e-a010-365a4ccb3a17.png)

3. プリンシパルを作成します。同じ画面の中ほどから、任意の名前でプリンシパルを作成します。続いて指定ログイン情報を作成します。ログインユーザに応じた分岐はないので、ID種別は`指定ユーザ` にします。アクションから `認証` をクリックします。`apex-mock-webservice` をデプロイした組織ログインし、アクセスを許可してください
![Principals](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/0b455a1a-5cf2-cd0e-6d0f-c1225151d22b.png)

4. 続いて、指定ログイン情報を作成します。URL は、Apex REST の URL を設定します。外部ログイン情報欄は、直前に設定した外部ログイン情報を指定してください
![Named Credential](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184624/6aac13ac-d4d4-138a-898c-2eaaa632e8a1.png)

5. 権限セットを作成し、外部情報プリンシパルアクセスから、3. で作成したプリンシパルを利用可能にします。この権限セットをユーザに割り当てることでコールアウトできるようになります

6. これでようやく API モックを叩く準備が整いました。サンプルの Apex コードを作成します。`Mock Web Service　Setting` カスタムメタデータで設定したパスをエンドポイントに設定してみましょう。指定ログイン情報を使用したエンドポイントの設定は、`setEndPoint('callout:指定ログイン情報名/パス')` という書き方になります
    ```apex
    HttpRequest req = new HttpRequest();
    req.setMethod('GET');
    req.setEndpoint('callout:ApexMockWebService/user/0001');
    HttpResponse res = (new Http()).send(req);
    System.debug(res.getBody()); // 対応する JSON ファイルがレスポンスになるはず
    ```
    対応する JSON ファイルの内容がレスポンスとして得られるはずです。実際の API が利用できるようになっても Apex コードの変更は不要で、指定ログイン情報や外部ログイン情報の設定を実際の API に合わせればOKです。(とすると手順の指定ログイン情報の命名はあまり良くないのですが、ご容赦を)
    
## おわりに
比較的簡単な手順で固定値を返す API を作成し Salesforce からコールすることができました。レスポンスのファイルは別に必ずしも zip にしなくても良いのですが、JSON ファイルが多くなると `*.resource-meta.xml` ファイルが煩わしいので…。ぜひ試してみてください！