dackdive's blog

新米webエンジニアによる技術ブログ。JavaScript(React), Salesforce, Python など

[Python] HerokuからForce.com REST APIを叩く

というサンプルを作りました。

元々、Heroku から Force.com REST API を叩くサンプルは海外の方が Node.js と Java で作ったものがありまして、それをお手本に Python 版を書いてみたという経緯です。

Python 製の Salesforce ライブラリについては以前調べてましたが

その中で有力と思われた simple-salesforce を使ってます。

また、simple-salesforce だけだと OAuth2 認証の部分がなさそうだったので
そこについては salesforce-oauth2 というサンプルコードを使わせてもらってます。

使い方

README.md を読めばとりあえず動かすことができると思います。
はじめに Salesforce 側で接続アプリケーションを作成して consumer key と consumer secret を取得する必要があります。

コード(抜粋)

フレームワークDjango を使っているのでファイルがたくさんありますが、重要なのは app/auth.pyapp/views.py ぐらい(および、View で使用しているテンプレート html ファイル)です。
app/auth.pysalesforce-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_KEYCONSUMER_SECRET が設定されているか確認します。
最初にアクセスした時は設定してないと思いますので、後述する setup ページにリダイレクトします。

(2)
クエリパラメータに code がないので、まずは認証用 URL を取得してそちらにリダイレクトします。
認証用 URL とは

https://login.salesforce.com/services/oauth2/authorize

のことです。ここに consumer key やらリダイレクト URI などを指定してアクセスします。

(3)
Salesforce 側で認証が完了するとリダイレクト URIcode パラメータつきで戻ってきます。

(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