2011年12月18日日曜日

Titanium Salesforce module(SalesForce Toolkit for Appcelerator)の使い方 (2)

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
前回のサンプルアプリをちょっと時間が経ってから動かしてみると、

[ERROR] In the error handler looking for a 401, and have a 401

[ERROR] Handleing the 401 error...

とかエラーが出てしまい動きません…

force.comからOAuth2で取得したaccess tokenには有効期限があるのでrefresh tokenを使って再度取得する必要があるのですが…そこが動作していないようです。
force.comのOAuthについて詳しくはこちらで

エラーのコールバックも呼んでくれないので有効期限切れたかどうかも検出できず…

なんとかhackできないかと、こんな感じでモジュールの中身をダンプしてみます。

var FDC = require('com.salesforce');
for (var i in FDC.ForceOAuth) {
 Ti.API.debug(i + ':' + FDC.ForceOAuth[i]);
}

最終的にREST APIの呼び出しはここに来るようです。
[DEBUG] makeRestCall:function (path, callback, error, method, payload, retry) {var restUrl=Ti.Network.decodeURIComponent(fa.instanceUrl)+'/services/data'+path;var xhr=Ti.Network.createHTTPClient();xhr.onload=function(){Ti.API.info("REST Response: "+this.responseText);var data="";if(this.responseText){data=this.responseText;}

callback(data);};xhr.onerror=function(e){Ti.API.error("XHR, error handler..."+"\nDbDotCom.REST.OAuth.refreshToken: "+fa.refreshToken+"\nretry: "+retry+"\n e: "+e.error+"\nXHR status: "+this.status);if(!fa.refreshToken||retry){error(e.error);}else{Ti.API.error("In the error handler looking for a 401, and have a "+xhr.status);if(xhr.status===401){Ti.API.error("Handleing the 401 error...");exports.refreshAccessToken(function(oauthResponse){Ti.API.error("Refresh response... "+oauthResponse);fa.makeRestCall(path,callback,error,method,payload,true);},error);}else{Ti.API.error("Not a 401 error, re-throwing...");error(e);}}};if(fa.usePostBin===true){restUrl="http://www.postbin.org/135onm5";}

xhr.open(method||"GET",restUrl,true)

Ti.API.info("Rest url: "+restUrl);xhr.setRequestHeader("Authorization","OAuth "+Ti.Network.decodeURIComponent(fa.accessToken));xhr.setRequestHeader("Content-Type","application/json");xhr.send(payload);}

出てるログから、exports.refreshAccessToken()の呼び出しの中でエラーが起きてコールバックまで戻って来ないようです。

よく考えたら、OAuth2のrefresh tokenによるaccess token再取得時にはclient secretが必要なはずなのに、モジュールのパラメータなどでどこにもセットしていないのでrefreshできるわけないですね… 未実装なんでしょうか?
※追記:secret要らない仕様に変わってました
ダンプしたソースを参考に、こんな感じでhackしてみました。
※モジュール内で定義されてるobjectのプロパティは動的に書き換えられない?&スコープ的にアクセスできない変数があったので結構無理矢理

使い方は、requireした後にこのファイルをincludeしてパッチを当て、ForceOAuthの代わりにForceOAuth2を使うようにします。ForceOAuth2.openのパラメータにはclient idとclient secretを渡す様にします。

var FDC = require('com.salesforce');
Ti.include('fdc-patch.js');
FDC.ForceOAuth2.open('CLIENT_ID');

パッチしたポイントは2つ。refreshAccessTokenをclient secretを使用して動作する様にしたのと、REST APIのパスの固定部分に /data が含まれていたのを /apexrest が呼べる様に /services までとしたこと。

Winter '12でリリースされたApex RESTを使って公開したAPIのURLは、$instance_url/services/apexrest/... となるので、FDC.ForceOAuth2.makeRestCall('/apexrest/myapi', callback) の様な感じで使える様になります。

marketplaceのモジュールのページには、"This toolkit is maintained by the community and sponsored by salesforce.com. Salesforce.com does not officially support this product."とか書いてあるんですが、パッチとか提供したい場合どこに連絡すればいいんでしょうか… githubとかにソース上がってればforkするのに…

この記事はForce.com Advent Calendar 2011に参加しています。

2011年12月8日木曜日

Titanium Salesforce module(SalesForce Toolkit for Appcelerator)の使い方

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
最近、Titanium Mobileでforce.comのREST APIを使用したiPhoneアプリを作ってます。

Titanium Mobileについての基本的な知識やTitanium Studioの使い方はある程度知ってる前提で書いているので、Titaniumについて知りたい方は「Titanium Mobileで作る! iPhone/Androidアプリ」などを参考にどうぞ。

Titaniumにはmarketplaceがあり、ソフトウェア部品としてのモジュールや、アプリのテンプレートなどをダウンロードできます(有償のもアリ)。
うまいこと目的に合ったモジュールを見つけて利用すれば開発スピードは向上する…はず…

Salesforce用のモジュールも用意されているので、今回の開発にはこれを利用しました。が、ドキュメントが大して無かったりで取っ掛かり苦労したのでメモとして残しておきます。
今回は、モジュールを使うまでの準備と、サンプルコードの実行まで。