というサンプルを作りました。
元々、Heroku から Force.com REST API を叩くサンプルは海外の方が Node.js と Java で作ったものがありまして、それをお手本に Python 版を書いてみたという経緯です。
- (Node.js) Salesforce REST APIs – From Zero to Cloud to Local Dev in Minutes – James Ward
- (Java) Quick Force Java – Getting Started with Salesforce REST in Java – James Ward
Python 製の Salesforce ライブラリについては以前調べてましたが
その中で有力と思われた simple-salesforce を使ってます。
また、simple-salesforce だけだと OAuth2 認証の部分がなさそうだったので
そこについては salesforce-oauth2 というサンプルコードを使わせてもらってます。
使い方
README.md を読めばとりあえず動かすことができると思います。
はじめに Salesforce 側で接続アプリケーションを作成して consumer key と consumer secret を取得する必要があります。
コード(抜粋)
フレームワークに Django を使っているのでファイルがたくさんありますが、重要なのは app/auth.py
と app/views.py
ぐらい(および、View で使用しているテンプレート html ファイル)です。
app/auth.py
は salesforce-oauth2 のコードそのままです。
http://localhost:5000 にアクセスした時に対応するのが以下の IndexView
です。
class IndexView(View): template_name = 'app/index.html' def get(self, request, *args, **kwargs): if not is_setup(): # (1) return redirect(reverse('app:setup')) connection = SalesforceOAuth2( client_id=os.environ.get('CONSUMER_KEY'), client_secret=os.environ.get('CONSUMER_SECRET'), redirect_uri=REDIRECT_URI) if 'code' in request.GET: # (3) res = connection.get_token(request.GET['code']) for k, v in res.items(): logger.debug('%s: %r' % (k, v)) # (4) sf = Salesforce( instance_url=res['instance_url'], session_id=res['access_token']) res = sf.query('SELECT id, name, type, industry, rating FROM Account') records = res['records'] return render(request, self.template_name, {'records': records}) else: auth_url = connection.authorize_url(scope='full') # (2) logger.debug('redirect to: {}'.format(auth_url)) return redirect(auth_url)
(1)
is_setup()
では変数 CONSUMER_KEY
と CONSUMER_SECRET
が設定されているか確認します。
最初にアクセスした時は設定してないと思いますので、後述する setup ページにリダイレクトします。
(2)
クエリパラメータに code
がないので、まずは認証用 URL を取得してそちらにリダイレクトします。
認証用 URL とは
https://login.salesforce.com/services/oauth2/authorize
のことです。ここに consumer key やらリダイレクト URI などを指定してアクセスします。
(3)
Salesforce 側で認証が完了するとリダイレクト URI に code
パラメータつきで戻ってきます。
(4)
code
を元にトークンを取得します。そのときに instance url も返されるので、この2つがあると username と password 無しで simple-salesforce が利用できます。
また、/setup
にアクセスしたときは以下の SetupView が使われます。
class SetupView(View): template_name = 'app/setup.html' def get(self, request, *args, **kwargs): if is_setup(): return redirect(reverse('app:index')) hostname = request.META['HTTP_HOST'] is_local = hostname.find('localhost') == 0 heroku_app_name = None if hostname.find('.herokuapp.com') > 0: heroku_app_name = hostname.replace('.herokuapp.com', '') context = { 'oauth_callback_url': REDIRECT_URI, 'is_local': is_local, 'heroku_app_name': heroku_app_name, } return render(request, self.template_name, context)
こっちはテンプレートを見るとわかりますが設定手順を表示するようになっていて、無事に consumer key と secret を設定できると /
にリダイレクトされます。
ローカルの時と Heroku にデプロイした時で consumer key, consumer secret の設定方法が違うので、ホスト名を見て手順の内容を出し分けてます。
TODOというかよくわからないこと
このサンプルだと毎回 code
取得 → code
からアクセストークン取得 → アクセストークン使ってデータ取得 を行ってますが
アクセストークンは保存して期限が切れるまで使いまわし、切れたらリフレッシュトークンを使って再度トークンを要求
というのが OAuth2 のフローとしては正しい気がします。
(私の理解が間違ってるかもしれませんが。。。)
が、アクセストークンを DB に保存するとなるとセキュリティのこととか考える必要もありそうなので、とりあえずはここまで。
リファレンス
Python ということでこちらも参考になりました。
Developer Blog | Salesforce Developers