Excel VBAからSalesforceのREST APIを実行してみた

## 初めに
この記事はSalesforce開発者向けの記事です。
今回は、Excel VBAとSalesforceを連動する方法を紹介します。
ExcelとSalesforceを連動すればアプリケーションの開発の幅が広がるので、是非参考にしてみて下さい。

## SalesforceとExcelを連動するには
Salesforceには外部アプリケーションからSalesforceのデータや機能にアクセスするためのREST APIやSOAP APIが豊富に備わっています。今回の記事ではExcel VBAからSalesforceのREST APIを用いてSalesforceのデータや機能にアクセスする方法を紹介します。

[SalesforceのAPI]
https://help.salesforce.com/s/articleView?id=sf.integrate_what_is_api.htm&type=5

## SalesforceのAPIを利用するための準備
SalesforceのAPIはOAuth2.0認証(オーオース2.0)が採用されています。
なので、SalesforceのAPIを利用する前にOAuth2.0の準備が必要になります。

### OAuth2.0認証とは
OAuth2.0認証は平たく言うと、セキュリティ観点よりAPIを実行する人が、自分でSalesforceにログインしないとAPIを実行出来ない様にする仕組みです。Salesforceの画面を見るには、Salesforceのログイン画面からログインを行います。同じ様にAPIを利用する場合にも、Salesforceのログイン画面でログインを行う必要があります。

OAuth2.0については他の方が記事にされていますので、そちらを参照して下さい。
https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f

### 接続アプリケーションの作成
OAuth2.0を用いて外部アプリケーションからSalesforceのREST APIを実行するには、Salesforce上に「接続アプリケーションを作成」します。
接続アプリケーションの作成方法は以下の通りです。

[接続アプリケーションの作成手順]
1. Salesforceの設定画面よりアプリケーションマネージャーを開く → 新規接続アプリケーションを押下する
![新規接続アプリケーションの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540922/d5bb94a8-7836-1425-73c8-0ddbd703a807.png)
2. 接続アプリケーションの設定を入力し保存ボタンを押下する
![接続アプリケーションの設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540922/c50e2e6d-4a74-f079-935f-0d7622bee4c1.png)
[接続アプリケーションの設定]
![接続アプリケーションの設定表.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540922/769f00d2-7095-fdd7-11bb-9a9c8757a3c3.png)
3. 「コンシューマーの詳細を管理」ボタンを押下する
![クライアントID確認画面を開く.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540922/f6281f5b-e4a6-8139-2a0b-f9d14b7883f8.png)
4. クライアントID、クライアントシークレットを確認する
![クライアントIDの確認.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540922/2d5ec77e-2e50-55e3-33d7-da68949d5bb1.png)

## Excel VBAの作成
REST APIを実行するsalesforce側の設定が完了しましたので、いよいよExcel VBAを作成します。

### OAuth2.0ログイン機能の作成
※ OAuth2.0について理解がある前提で記載します。

前述のクライアントID、クライアントシークレットを用いて、REST APIを実行するためのアクセストークンを取得する機能を作成します。リフレッシュトークンの取得も出来ますが、ここでは割愛します。

* １．Selenium Basicの準備
OAuth2.0を行うにはExcel VBAからブラウザ操作が必要です。
ここではSelenium Basicを用いますので、Selenium Basicをインストールします。
※Excel VBAからブラウザ操作を行う他の方法もありますが、2023/11/12時点で一般的な方法として、ここではSelenium Basicを用います。
　
[Selenium Basicのインストール]
Selenium Basicについては沢山の方がノウハウを公開しています。
このあたりの記事を参考にSelenium Basicをインストールして下さい。
https://qiita.com/Rui010/items/0a226c26fe44e56c9c39

* ２．VBAのコード作成
以下のコードを作成します。
```
'----------------------------------------
' Salesforceの組織にOAuth2.0で接続する
' 引数：aClienId  OAuthクライアントID
'      aClienSecret OAuthクライアントシークレット
'      aRedirectURL  OAuthリダイレクトURL
' 戻り値：アクセストークン
'----------------------------------------
Public Function GetSalesforceAccessToken( _
                    ByVal aClienId As String, _
                    ByVal aClienSecret As String, _
                    ByVal aRedirectURL As String) As String
    Dim oWebDriver As Selenium.WebDriver
    Dim i As Long
    Dim sURL As String
    Dim sCode As String
    Dim sResponse As String
    Dim sAccessToken As String
    
    ' Salesforce OAuth 2.0認証エンドポイント
    sURL = "https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id=[クライアントID]&redirect_uri=[リダイレクトURL]&scope=full"
    sURL = Replace(sURL, "[クライアントID]", aClienId)
    sURL = Replace(sURL, "[リダイレクトURL]", aRedirectURL)
    
    '認証コードを取得する
    With New Selenium.WebDriver
        .Timeouts.PageLoad = 10000 'ページロード待機10秒
        .Start "chrome"

        'Salesforce OAuth認証画面起動
        .Get sURL
        For i = 0 To 100
            If InStr(.Url, "localhost") > 0 Then
                Exit For
            End If
            .Wait 1000
        Next i
        'ログイン成功した場合、認可コード取得
        sCode = Trim(ModUtil.RegReplace(.Url, ".*code=", ""))
        .Quit
    End With
    
    If sCode = "" Then
        GetSalesforceAccessToken = ""
        Exit Function
    End If
    
    '認証コードをアクセストークンに変換
    With CreateObject("WinHttp.WinHttpRequest.5.1")
        .Open "POST", "https://login.salesforce.com/services/oauth2/token", False
        .setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
        .send "grant_type=authorization_code&client_id=" & aClienId & "&client_secret=" & aClienSecret & _
            "&redirect_uri=" & aRedirectURL & "&code=" & sCode
        sResponse = .responseText
        sAccessToken = RegReplace(sResponse, ".*""access_token"":""", "")
        sAccessToken = RegReplace(sAccessToken, """.*", "")
    End With
    
    GetSalesforceAccessToken = sAccessToken    
End Function

'----------------------------------------
' 正規表現による置換
' 引数：aOrgStr 置換対象を含む文字列
'       aRepStr 置換対象文字列
'       aTargetStr 置換文字列
' 戻り値：置換後の文字列
'----------------------------------------
Public Function RegReplace( _
                    ByVal aOrgStr As String, _
                    ByVal aRepStr As String, _
                    ByVal aTargetStr As String) As String
    With CreateObject("VBScript.RegExp")
        .Pattern = aRepStr
        .IgnoreCase = False
        .Global = True
        RegReplace = .Replace(aOrgStr, aTargetStr)
    End With
End Function

```

* ３．コードの実行
上記ソースが準備出来たら以下のコードを作成し実行します。
```
  Dim sAccessToken As String
  sAccessToken = GetSalesforceAccessToken( _
        クライアントID, クライアントシークレット, コールバックURL)
```

* ４．実行結果
Salesforceのログイン画面が起動し、ログインを行うとAccessTokenが返却されます。
![SFDCログイン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540922/dd13ec1a-cfd4-8be0-2808-861236a86c55.png)

### REST APIの実行
上記で取得したアクセストークンを用いてREST APIを実行します。
ここでは、REST APIを用いてSalesforceからオブジェクトの一覧を取得します。

REST APIは沢山ありますので、個々のAPIの利用方法はSalesforceの公式サイトを参照して下さい。
https://developer.salesforce.com/docs/atlas.ja-jp.api_rest.meta/api_rest/resources_list.htm

* １．VBAのコード作成
以下のコードを作成します。
```
'----------------------------------------
' SFDCからオブジェクト一覧を取得する
' 引数：aAccesssToken アクセストークン
' 戻り値：オブジェクト基本情報(Json)
'----------------------------------------
Public Function LoadObjList(ByVal aAccesssToken As String) As Object
    Dim sEndpoint As String
    Dim AccessToken As String
    Dim ObjectName As String
    Dim sResponse As String
    
    With CreateObject("MSXML2.ServerXMLHTTP.6.0")
        ' REST APIの実行
        ' ※APIのエンドポイントは組織ごとに異なります。先頭のxxxx.salesforce.comは組織のドメインです。
        sEndpoint = "https://xxxxx.my.salesforce.com"
        .Open "GET", sEndpoint & "/services/data/v53.0/sobjects", False
        .setRequestHeader "Authorization", "Bearer " & aAccesssToken
        .send ""
        LoadObjList = .responseText
    End With
End Function
```
* ２．コードの実行
上記ソースが準備出来たら以下のコードを作成し実行します。
```
  Debug.Print LoadObjList(アクセストークン)
```
* ３．実行結果
以下の様なJSONレスポンスが取得出来ます。
```
{"encoding":"UTF-8","maxBatchSize":200,
 "sobjects":[
   {"activateable":false,
    "associateEntityType":null,
    "associateParentEntity":null,
    "createable":false,
    "custom":false,
    "customSetting":false,
    "deepCloneable"
 ・・・
```

## REST APIで何が出来るの？
REST APIを用いてExcelとSalesforceを繋げて何が出来るか？
それはアイディア次第です。
アイディア次第で出来ることは無限です。

例えば、以下の様なことが考えられます。
* ExcelのSalesforce設計書の自動生成
* データの一括ダンプツール
* 業務用のExcel帳票を自動で作成
* データの一括登録ツール

## Excel VBAからSalesforceのREST APIを利用する場合の補足
今回の記事では触れませんでしたが、Excel VBAからSalesforceのREST APIを利用する場合は以下の考慮や注意が必要です。

1. プロキシを利用している場合は、VBAプログラムでプロキシの考慮が必要
2. Salesforceの本番、SandboxでOAuth、REST APIのURLが異なる
3. JSON文字列をVBAの配列や辞書形式に変換が必要（JsonConverterというVBAライブラリを用いるのが楽です）
4. OAuth、REST APIでエラーが発生した場合のエラー判定処理
5. クライアントID、クライアントシークレット、アクセストークンの秘匿化
6. リフレッシュトークンによるログイン時間の延長の考慮(セキュリティの観点で利用するかは検討要)
7. OAuthで付与する権限範囲(full権限ではなく用途に合わせて制限すべき)
8. 全角文字や特殊文字の考慮（URLエンコード/デコード）
9. Salesforceのガバナ制限の考慮（SOQLの実行制限などは基本的にApexコードと同じです）