SalesforceAPIをRubyから叩けるようになるまで

思ったより色々揃えたりするのに苦労してしまったので備忘録です。
Rubyというより9割はsalesforceの画面の話になってしまいました...。
また、 `OAuth token authentication` でなく `Username/Password authentication` について調べたものです。

# gem 'restforce' を使う
- https://github.com/restforce/restforce
- 導入はREADMEの[Installation](https://github.com/restforce/restforce#installation)に従います。
- ちなみに、以前は `databasedotcom` というgemがよく使われていましたが、DEPRECATED扱いになったようです。([github参照](https://github.com/heroku/databasedotcom))

> DEPRECATED: Please use restforce instead.


# Sandboxに繋げたい
ところで本番用のデータをいきなりAPIで取りにいったり更新したりするのは恐ろしいですね。なので、開発途上でお試しでAPIを叩くものとして別の環境(Sandbox)を用意します。新規Sandboxを作った時点でのカスタム項目の定義などを引き継ぐことができます。
Developer / Developer Pro / Partial Copy / Full と4種類あるようですが、購入しているライセンスによって使えるものが違うようです。今回は中身が空でよくて定義だけコピーできていればいいや、ということでDeveloperを選択しました。Sandbox名を入れて選択すると準備が始まります。
しばらくすると `Sandbox: #{Sandbox名} の使用準備が整いました` のメールが届きます。内容をチェックすると、Sandbox環境のURLおよびログインユーザ名の説明が乗っています。説明にしたがってSandbox環境へログインできることを確認してください。

ちなみに、Salesforceは2018年夏の時点ではLightningとClassicのUI切り替えができ、それぞれ目的の設定画面へたどり着くための導線が違うという状況なため、念のためどちらも併記していきます。自分はどこになんのリンクがあるのか探すのに一番時間を使いました...。

- Classic

salesforceにログイン > 設定① > リリース > Sandbox② > 新規Sandbox③ から作成。②はクイック検索に入力でも探すことができる。

<img width="1070" alt="Sandbox___Salesforce_-_Enterprise_Edition.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/8a38ef3e-dabc-49cc-d493-1dc67f147fed.png">



- Lightning

salesforceにログイン > 歯車マーク > 設定① > 環境 > Sandbox② > 新規Sandbox③ から作成。②はクイック検索に入力でも探すことができる。

<img width="1051" alt="sandbox_salesforce_lightning.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/0cfed0a5-f32c-a87b-a2be-f66c78845d2e.png">




# 諸々のキーを揃える

Rubyで `Restforce.new` するにあたって、諸々のキー類が必要です。
自分の場合は、`Username/Password authentication` の方法を用いました。
Sandbox環境と本番環境ではそれぞれ別の値を揃える必要があります。

```ruby
@client = Restforce.new({
    username: 'username',
    password: 'password',
    security_token: 'security_token',
    client_id: 'client_id',
    client_secret: 'client_secret',
    instance_url: 'instance_url',
    host: 'host',
    api_version: 'api_version',
})
```

## username, password
通常通りSalesforceにログインする際のメールアドレスおよびパスワードです。
Sandboxではメールアドレスに `.#{Sandbox名}` がくっついているはずです。


## security_token
セキュリティトークンを発行します。
参考: https://success.salesforce.com/answers?id=90630000000glADAAY


- Classic

ヘッダーの `ユーザ名▼` > 私の設定 > 個人用 > 私のセキュリティトークンのリセット を選択

<img width="1228" alt="classic_security_token.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/76aa9ffe-fd2e-fa7c-0dc0-166b17c8dd29.png">



- Lightning

ヘッダー右上のユーザアイコン > 設定 > 私の個人情報 > 私のセキュリティトークンの... を選択
 
<img width="1229" alt="lightning_security_token.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/b24092f9-130c-49fd-acf1-8458785ae0ca.png">


`セキュリティトークンのリセット` のボタンを押すと、利用者宛にメールが届き、セキュリティトークンとなる英数字文字列を得ることができます。最初は気にせず押して良いですが、トークンを何かに使い始めた後に押すのは危険なボタンですね。


## client_id, client_secret
アプリケーションを作成して、client_id, client_secretの文字列を発行します。
参考: https://feedback.uservoice.com/knowledgebase/articles/235661-get-your-key-and-secret-from-salesforce


- Classic
  - 設定 > ビルド > 作成 > アプリケーション
  - 最後のコンテンツに「接続アプリケーション」があるので「新規」
- 新規接続アプリケーションの作成を行う。次の項目を埋める。残りは任意。
    - 接続アプリケーション名: 任意の名前
    - API参照名: 任意の名前
    - 取引先責任者メール: 開発担当者のメールアドレス(?)
    - API(OAuth設定の有効化) のチェックボックスをONにする。
    - コールバックURL: http://localhost:3000/oauth/salesforce/callback
        - 利用されないので任意のURLを入れて良いらしい。参考参照。
    - 選択したOAuth範囲: フルアクセス(full)
    - Webサーバフローの秘密が必要: ON


<img width="1214" alt="アプリケーション___Salesforce_-_Enterprise_Edition.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/5585ea43-59a0-620a-ff2d-5c0b63a3481b.png">



- Lightning
    - 画面参考: https://help.salesforce.com/articleView?id=connected_app_create.htm&type=5
    - 歯車マーク > 設定 > アプリケーション > アプリケーションマネージャ > 新規接続アプリケーション
    - あとはClassicのフローと同様

<img width="1108" alt="アプリケーションマネージャ___Salesforce.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/3d50c9a7-caf3-d455-bb83-46edeee3c7f8.png">


作成が完了したら、次の操作でアプリケーションの詳細を閲覧します。

- Classic
    - 設定 > ビルド > 作成 > アプリケーション > 接続アプリケーションの欄にある作成したアプリの名前を選択
- Lightning
    - 歯車マーク > 設定 > アプリケーション > アプリケーションマネージャ > 作成したアプリの名前の項目の最右▼ > 参照 を選択
 
詳細の `API(OAuth設定の有効化)` の項目に `コンシューマ鍵` と `コンシューマの秘密(クリックして公開)` が表示されます。
`コンシューマ鍵` が `client_id`、`コンシューマの秘密` が `client_secret` として使うことができます。


<img width="1252" alt="screencapture-cs31-salesforce-app-mgmt-forceconnectedapps-forceAppDetail-apexp-2018-07-12-22_30_39_png.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/918c9edb-a947-a6cf-759a-e2c95213cdf9.png">



## instance_url
環境のURLを指定します。本番環境とSandboxとで提供されるURLが異なります。
利用しているページの `https://xxxxx.salesforce.com` の `xxxxx` のところが違うはず。

## host
本番環境では `login.salesforce.com` (default設定)、
Sandbox環境では `test.salesforce.com` です。


## api_version
任意のrestforceの公式が最新のAPIバージョンを使うように推奨しています。
https://github.com/restforce/restforce#api-versions
以下を参考にして、APIバージョンを判別します。
https://help.salesforce.com/articleView?id=000199268&language=ja&type=1

- 設定 > 開発 > API > Enterprise WSDL の生成 > Generate を選択
- XMLが開き、先頭のコメントアウト領域にその時の最新バージョンが表記されている。

> Salesforce.com Enterprise Web Services API Version 43.0

# 全部そろった！（ここから実装ははじまる...）
無事全部そろったので、 `Restforce.new` を実施して `create` や `upsert` などのリクエストを投げられるようになったはずです。
APIを活用してどんなことをやるかが重要なので、ようやくスタート地点です。
自分と同じようにスタート地点に立つ前に変な苦労にぶつかっている方の一助になればと思います。
また、実際にやってみて若干つまづいたところについて、トラブルシューティングとして記載しておきます。


# トラブルシューティング: invalid_grantエラー

```ruby
@client = Restforce.new({...})
@client.describe # 叩いてみる

Restforce::AuthenticationError: invalid_grant: authentication failure
from .../gems/restforce-2.5.3/lib/restforce/middleware/authentication.rb:28:in `authenticate!'
```

準備したキーの何かが間違えている、ということも考えられますが、自分の出くわした問題は設定都合でした。
解決参考: https://github.com/restforce/restforce/issues/169#issuecomment-110465110

- 設定 > 管理 > アプリケーションを管理する > 接続アプリケーション > ( `client_id, client_secret` を発行するために作ったアプリケーションを選択)
- ポリシーを編集 > 許可されているユーザが `管理者が承認したユーザは事前承認済み` になっている場合、 `invalid_grant` になってしまう。
- これを `すべてのユーザは自己承認可能` に修正、保存

```ruby

# うごきました！

@client = Restforce.new({...})
@client.describe

#=> [ ... (たくさんの情報が返ってくる!) ...]
```

# トラブルシューティング: Sandbox更新すると色々消えるので注意
`種別: Developer` のSandboxを使用して開発を始めたあと、更新を行うと次のようなことが起こりました。更新というよりも上書き新規作成という印象を感じます。（まだこの辺あんまり把握できていない...)

- 本番側で作った新しい項目が反映される
- Sandbox内に作成済みだったデータ類は全て削除される
- 同様にSandbox内に接続アプリケーションを作成していても削除されている
    - もともとの本番環境に先にSandbox用の接続アプリケーションを作っておけば引き継がれる様子だが、なんとなく事故の匂いがするのであんまりしたくない。Sandbox用のトークンを使って本番データにアクセスできる状況は回避したい。

そもそもSandboxの有用な使い方を完全に履き違えている可能性もありそうです...。 :thought_balloon: 


# トラブルシューティング: レコードタイプのID値を知りたい
Salesforceにて `レコードタイプ` というものを作ることができ、 Account(取引先)を作るときに指定したりできるのですが、名前でなくID値で指定する必要がありました。
しかしこのID値がSalesforce内からは把握できなかったため、下記ヘルプを参考にしつつ調べました。
https://help.salesforce.com/articleView?id=000213934&language=ja&type=1

- 下記のワークベンチにアクセス
    - https://workbench.developerforce.com/login.php
- 指定してLogin
    - Environment: 対象の環境(Production / Sandbox)
    - API version: 一応最新の43.0を選択
    - termsに同意チェックする
    - 入力したら Login with Salesforce
- perform指定
    - Jump to: SOQL Query
    - Object: (Account) (= 取引先データを調べたかったらコレ)
- Enter or modify a SOQL query below: に下記クエリを投入
    - `SELECT Name, Id, SobjectType FROM RecordType Where SobjectType='Account'`
- レコードタイプのIdを知ることができた！

<img width="514" alt="query_result.png" src="https://qiita-image-store.s3.amazonaws.com/0/24870/3294ee9e-fbdc-0555-f5fc-8d13e7cd2386.png">



# トラブルシューティング: テスト実施で困ったことと対策
Restforceを使っている周辺のテストを作成しところ、ローカル開発環境ではテストは通ったものの、いくつかの問題点がでてきたので、それぞれ対策しました。

- 問題1: キー類を書いたファイルをGit管理下に配置したくない
    - そのため、Git管理しているのはサンプルファイルで、その中には `client_id: piyo` とか適当な文字列が入っている 
- 問題2: CIにそのままテストを回させたら当然落ちた。
    - そもそもサンプルファイルしか管理していないので設定ファイルが見当たらなかった
    - Sandboxに接続できる設定ファイルを置いてもいいけど、Sandboxとはいえ、CIからあんまりリクエストがたくさん飛ぶのもどうなんだ。

### キーをGit管理したくない
CIの設定でSalesforceキーのサンプルファイルをコピーしてからテストすることにしました。
とりあえず、ファイルが見つからなくてテストが落ちるということはなくなりました。

```
$ cp configfile.sample configfile
```

### 保持しているキーの値が嘘っぱちなのでテストが落ちる
VCRを使ってローカル環境で実施したときのリクエスト結果をファイルに保持し、CIではそれを使わせるようにしました。

- 参考
    - [VCRを使うとRSpecのWebmockの作成が超絶楽になった！ | 酒と涙とRubyとRailsと](https://morizyun.github.io/blog/webmock-vcr-gem-rails/)
    - [VCRで外部APIとのやりとりを記録する - Qiita](https://qiita.com/sawamur@github/items/e6fa877e35b383903e2e)
    - [VCRで外部APIダンプを作って使う - Qiita](https://qiita.com/satoruk/items/d137f0d9fbf7c286fd46)
    - [VCRを使って開発中にあほみたいにリクエストを飛ばさないようにする - かずおの開発ブログ(主にRuby)](http://kazuooooo.hatenablog.com/entry/2015/12/05/225708)

```ruby
VCR.use_cassette 'account_find' do
  @client = Restforce.new({...})
  @client.find('Account', 'salesforce_id')
end
```

ただし、Restforce自体が飛ばすリクエストURLが一致していないと `VCR::Errors::UnhandledHTTPRequestError` が発生してしまうので、キーの値は次のように設定しました。

```ruby
@client = Restforce.new({
    username: 'ダミーでOK',
    password: 'ダミーでOK',
    security_token: 'ダミーでOK',
    client_id: 'ダミーでOK',
    client_secret: 'ダミーでOK',
    instance_url: 'https://xxxxx.salesforce.com', # Sandboxの値を入れる
    host: 'test.salesforce.com', # 指定する
    api_version: '43.0', # 指定する
})
```

このキー設定のファイルと、VCRの結果で生成されたファイルをGit管理下においたところ、テストが無事通過しました。



# 全体を通しての参考
- [RubyからSalesforce REST APIを実行する (restforceを使用してSalesforceカスタムオブジェクトへレコードを追加する) - Qiita](https://qiita.com/na0AaooQ/items/bf9a33d76b7120522389)
- [databasedotcom(gem)、restforceでsalesforceに繋いでみる - Qiita](https://qiita.com/msky026/items/0dbb22f59d61e578f77c)