2014-06-13

Google Apps ScriptからSalesforceのAPIを叩く【access_token取得まで】

前回はVBAでSalesforceの環境を弄りましたが、VBAだとローカル環境なスクリプト言語なので、操作性やパフォーマンスの良さ等のネイティブな良さを活かせる一方で、ネットに繋がっている環境でいつでも、どこでも、どんな端末でも動かしたいという要件は満たせません。

そんなときにはGoggle Apps Scriptが便利です。Google Apps ScriptはGoogleのサービスを拡張できたり、Webサービス作れたりするPaaSなスクリプト言語になります。

スプレッドシート等のGoogleサービスに対するマクロみたいなことが出来たり、単体でWebサービス作ったり、簡単なcronなバッチ処理もこなすことができます。

ということで、Google Apps ScriptからSalesforceのAPIを叩いてみました。今回はOAuthのaccess_token取得まで。

Salesforceの接続アプリケーションを作成

毎度おなじみ接続アプリケーション。今回はフルアクセスとrefresh_tokenの権限を付与しました。コールバックURLはこの時点ではテキトーなもので良いです。

connectapp-gas

スプレッドシートの新規作成&スクリプトエディタ立ち上げ

まずはテキトーなスプレッドシートを作成

g-drive

スプレッドシートを開いたら、ツールメニューからスクリプトエディタを立ち上げる

gas_spreadsheet

スクリプトエディタでスクリプトを作成

以下のスクリプト・テンプレートhtmlを作成します。REDIRECT_URIはこの時点ではテキトーなもので良いです。

salesforce.gs

var AUTHORIZATION_URL = "https://login.salesforce.com/services/oauth2/authorize?response_type=code&client_id={client_id}&redirect_uri={redirect_uri}&state={state}";
var ACCESS_TOKEN_URL = "https://login.salesforce.com/services/oauth2/token";
var CLIENT_ID = "input your client_id";
var CLIENT_SECRET = "input your client_secret";
var REDIRECT_URI = "https://script.google.com/macros/s/********************/usercallback";

/**
 * メニューの設定
 */
function onOpen() {
  SpreadsheetApp.getActiveSpreadsheet().addMenu("Salesforce Utility", [
    {name:"OpenDialog", functionName: "openDialog"}
  ]);
}

/**
 * ダイアログを開く
 */
function openDialog() {
  var prop = PropertiesService.getUserProperties();
  var template = HtmlService.createTemplateFromFile("dialog");
  var authorization_url = AUTHORIZATION_URL;
  authorization_url = authorization_url.replace("{client_id}", CLIENT_ID);
  authorization_url = authorization_url.replace("{redirect_uri}", encodeURIComponent(REDIRECT_URI));
  authorization_url = authorization_url.replace("{state}", ScriptApp.newStateToken().withMethod("callback").withTimeout(120).createToken());
  
  template.authorization_url = authorization_url
  SpreadsheetApp.getActive().show(template.evaluate());
}

/**
 * OAuthでコールバックされたcode値を元にaccess_tokenを取得・保持する。
 */
function callback(e) {
  var code = e.parameter.code;
  var res = UrlFetchApp.fetch(ACCESS_TOKEN_URL, {
    "method" : "POST",
    "payload" : {
      "grant_type" : "authorization_code",
      "code" : code,
      "redirect_uri" : REDIRECT_URI,
      "client_id" : CLIENT_ID,
      "client_secret" : CLIENT_SECRET
    }
  });

  var prop = PropertiesService.getUserProperties();
  prop.setProperty("session_info", res.getContentText());

  return HtmlService.createHtmlOutput("<div>windowを閉じて!!</div>");
}

dialog.html

<a href="<?= authorization_url ?>">oauth</a>

処理の流れとしては

  1. onOpenでスプレッドシートを開いた時に独自メニューを作成
  2. 独自メニューからダイアログを呼び出して(openDialog)、authorization_urlをaタグに書き出し
  3. authorization_urlが埋め込まれたaリンクからOAuthを実行
  4. コールバックURL(https://script.google.com/macros/s/***/usercallback)でauthorization_codeを受け取ってaccess_tokenを取得
  5. 取得したaccess_token等の情報をUserProperty(ユーザごとのセッションストレージ的なモノ)に格納
となります。

GASの仕組みとしてhttps://script.google.com/macros/s/***/usercallbackのURLにアクセスした場合、stateパラメータによってプロジェクト内の任意の関数を呼び出すことが出来るというモノがあります。これを使ってOAuthでstateパラメータを付けてあげて、コールバックが返って来たらstate値から自動的にaccess_tokenを取得する関数を実行させています。

ということで今回の例のstate値はOAuth2.0のそもそもの用途であるCSRF対策と、任意の関数の自動実行という2つの意味合いを持ちます。

認可コールバック用のWebアプリケーションを作成

スクリプトエディタの[公開>ウェブアプリケーションとして導入]からGASをウェブアプリケーションとして公開します。

webapp-publish

導入ボタン押下すると、以下のようなダイアログが表示されるので、現在のウェブアプリケーションのURL(あるいは最新のコードリンクのURL)をコピー。

webapp-publish2

URLの形式が

https://script.google.com/macros/s/hogehoge/exec

になっているので、

https://script.google.com/macros/s/hogehoge/usercallback

といった形式に書き換えたURLをSalesforce接続アプリケーションのコールバックURLとして登録し、3のREDIRECT_URIも書き換えます。

スクリプトの実行

Salesforce UtilityっていうメニューからOpenDialogでダイアログを開いて、authorization_urlのリンクをクリックして、認可してコールバック先に戻ったら手動でダイアログ閉じると、UserPropertyにaccess_tokenやらrefresh_tokenやらが入ります。

あとは保持したaccess_tokenを使ってAPIを呼び出したり、access_tokenがexpireしたらrefresh_tokenで再取得する、といったいつも通りの使い方になります。

Google Apps Script実装の注意点

指定したhtmlを出力したり、テンプレートになるhtmlファイルに対して変数をバインディングして動的にhtmlを出力する等は出来ますが、出力するhtml、css、JavaScriptは全てcajaというサニタイズフレームワーク的なものによってゴニョゴニョされちゃいます。

結果としてJavaScriptが自由に使えなくなり、かなり不便です。具体的にはwindow.openやwindow.close等が使えなかったりタグでtarget="_self"って書いてもtarget="_blank"に書き換わっちゃったりします。

本当はOAuthで認可を得るために別ウィンドウを立ち上げる(window.open)や、画面遷移(location.hrefとかリンク)的なことをしたかったのですがcajaのサニタイズのため、タグのtarget="_blank"で認可画面を立ち上げてウィンドウのクローズは手動で行うことにしました。

次回はGoogleAppsScriptから色々とAPI叩いてスプレッドシートで遊んでみたいと思います。

参考URL

このエントリーをはてなブックマークに追加