databasedotcom(gem)、restforceでsalesforceに繋いでみる

この記事は [Heroku Advent Calendar 2015](http://qiita.com/advent-calendar/2015/heroku) の 23 日目の記事です。

## はじめに

HerokuからSalesforceのオブジェクトのデータを取りに行きたい場合、どうしますか？
Heroku Connectという選択肢がぱっと浮かびますよね？

今回のAdvent Calendarでも取り扱った記事がちらほらあります。

- [Heroku Connectの注意点](http://qiita.com/tarot/items/5241045ea96a8d91d630)
- [Heroku Connect についてのあれこれ](http://deferloader.blog.uhuru.co.jp/?p=6152)

Heroku Connectを使うとPostgreSQLからsalesforceのデータが拾えるからめっちゃ便利ですよね。

特に小細工とか必要ないですよね？

という訳でもうお開きにしていいですか。


いいわけ無いですか、そうですか。。

## Databasedotcom(gem)

以下の条件でデータを取得したい場合があったとします。

- Herokuからsalesforceにデータを取りに行きたい
- その逆のパターンはない
- salesforceのAPIは多少消費しても構わない
- 逆にあまりsalesforceに繋ぎに行く機会がないのでHerokuConnectでは手に余る

RubyのGemでdatabasedotcomというものがあります。
（因みにHeroku謹製のツールです）

[datbasedotcom](https://github.com/heroku/databasedotcom)

ざっくり言うと、AdtiveRecordライクにRailsのアプリからsalesforceのオブジェクトに接続することが出来ます。
注意点として、あくまでARライクなので、（特にARと継承関係とかあるわけでなく自力でfind等のメソッドを実装している）細かい点はどうにかしないと（query書くなどして）いけません。


### 使い方

### sf側での認証

この辺りはざっくりした説明で恐縮ですが、

接続 -> ビルド -> 作成 -> アプリケーション から 新規の接続アプリケーションを選択し、適当な名前で作成。
コンシューマ鍵と、コンシューマ秘密鍵を生成してください。
コールバックURLは`http://localhost/callback`とかでいいです。
後、各ユーザの「私の設定」から、アクセストークンの設定（設定したメールに届く）も必要です。

必要な情報を整理すると

- コンシューマ鍵（やたら長いトークン列）
- コンシューマ秘密鍵（数値の列）
- SFユーザID（メールアドレス形式のおなじみの）
- SFユーザパスワード
- SFユーザトークン

となります。(実際に使う場合はSFユーザパスワードにSFトークンをくっつけての運用になります)

### databasedotcomのインストールと設定

インストールの方法は例のごとくGemfileに以下の1行を追加し`bundle install`です。

```
gem 'databasedotcom'
```

使い方的にはgithubのreadmeに色々なパターンが書いてあるんですが、私だと大体以下の様な設定をしてます。
（見た感じ、設定をyamlに書いてロードさせる方法もあるっぽいですが確かめてはいません）

```
# 環境変数に入れといた、クライアントIDからsfパスまでを取り出す。
client_id = ENV['DATABASEDOTCOM_CLIENT_ID']
client_secret = ENV['DATABASEDOTCOM_CLIENT_SECRET']
sf_user = ENV['DATABASEDOTCOM_CLIENT_USERNAME']
sf_pass = ENV['DATABASEDOTCOM_CLIENT_AUTHENTICATE_PASSWORD'] # ここでのパスワードはパスワード + トークンです

# 認証
$sf_client = Databasedotcom::Client.new client_id: client_id, client_secret: client_secret
$sf_client.authenticate :username => sf_user, :password => sf_pass
```

グローバル変数に入れてるのはただ単に使いまわしのためです。

sfオブジェクトに接続する設定は上記で作ったクライアントで以下のように設定します。

```
hoge__c = $sf_client.materialize("Hoge_c")
```

意味があるのは右辺値の方で、materializeメソッドに、オブジェクト名（カスタムでもオリジナルでも可）を指定します。左辺値のインスタンス名は適当で構いません。（わかりやすければ）

全取得の例はこんな感じ。

```
@hoge = hoge__c.all #全取得
```

Hoge__cにuserid__cとuserpass__cというカスタム項目があって認証に使う例（要は複数の項目で絞り込み検索したい場合）だと

```
@user_info = hoge__c.find_all_by_userid__c_and_userpass__c(param['id'],param['pass'])
```

という感じになります。（strong parameterは省略）

あくまでARライクなので、whereなどはありません。
これ以上細かい条件を指定したい場合など、queryメソッドを使ってたりします。
この辺りの説明はreadmeに書いてないので、ソース（[ここ](https://github.com/heroku/databasedotcom/blob/master/lib/databasedotcom/client.rb#L203-L206)とか）を覗いて調べてました。

```
@hoge = $sf_client.query("select ID,Name,nanyakanya where Hoge__c where xxx and yyy and...")
```

## restforce

ARライクな使い方は特に不要で、単純なクエリを使用するだけならrestforceという選択肢もあります。

[restforce](https://github.com/ejholmes/restforce)

インストールの仕方はGemfileに
```
gem 'restforce'
```
で`bundle install`

認証の設定は私だと以下のようにしてます。

```
# sf_userなどの変数へのデータ取得は環境変数などから行ったものとして省略
$sf_client = Restforce.new :username => sf_user,
  :password       => sf_pass,
  :security_token => sf_token,
  :client_id      => client_id,
  :client_secret  => client_secret,
  :api_version => "34.0" # APIバージョンは新しめのにしといてください
```

restforceを使う場合は、SFパスワードとSFトークンは個別で設定してください。
（databasedotcomだとくっつけてますけど）

### restforceからの接続

使うとすればこんな感じになります。

```
hoge = $sf_client.query("select Id, Name from Hoge__c where userid__c = '#{id}' and userpass__c = '#{pass}'")
```

上記例だと思い切りSQLインジェクションの穴じゃないか！　と言われたら本当にその通りなのですが、プリペアドステートメント的な方法はないか調べたのですが、ちょっと見つからなかったので、今のところどうにもなりません。。

## API消費数について

メリットだけ見たら、databasedotcomの方が高機能だからこっちでいいじゃないかと思いますけど、databasedotcomについてはAPI消費数が不規則な増え方をする場合があったりします。
試しに認証 -> 適当なデータ取得 の流れを検証してみると、普通に考えればAPI消費数は1件（ちなみにどちらもREST APIを消費）のはずですが、databasedotcomだと1件に収まらないケースがある、ということだけ留意しておいてください。
確認方法として、レポートから管理レポートフォルダの過去7日間のAPI使用状況を見て判断してます。
（全く根拠の無い予測ですけど、もしかして参照関係になってるオブジェクトをまるごと引っ張ってくるタイミングでN+1問題的なループがどっかで発生してしまったかも、という気はしますが（APIの消費の仕方が一定じゃなかった理由はそれで説明はつきますが））