2013年12月11日 (水)

SalesforceとHeroku Postgresの同期

こんにちは。このエントリはSalesforce Advent Calendarの11日目です。


ノリでSalesforce Advent Calendarに手を挙げてしまいましたが、僕自身はSalesforceでアプリを作ったことは一度もありませんVisualForceは1時間で挫折しました。

そんな人間がSalesforceのAdvent Calendarで何を語ろうと言うのかというと、SOAP APIのJavaラッパーを作ったりもしているので、それを使ったSalesforceと外部RDBMSとの同期連携について書いてみようと思います。

※タイトルは半分釣りでHeroku Postgresと書いていますが他のRDBでも多分動きます。


★ところで Heroku Connect

本題に入る前にSalesforceとHeroku Postgresの同期と言えば先日のDreamForceでHeroku Connectが発表されました。

http://blogjp.sforce.com/2013/11/heroku1-.html

今のところ予告編だけで実際に試せるのは来年以降になるらしいですが、某エバンジェリストに聞いたところでは、これはSalesforce上のオブジェクトと同じ構造のテーブルをPostgres上に作成して双方向に同期するもののようです。

ちなみにガバナ制限は受けない某エバンジェリストは力強く言っていましたが、別の筋からはそんなの聞いてないという話もあり、真偽は定かではありません。(^^;

素晴らしいんですが、この枠組みだとまずSalesforceアプリありきなので、既存のHerokuアプリのデータをSalesforceに取り込みたい場合は使いにくいかもしれません。

今時のクラウドデータベースではストレージ容量は十分過ぎる程にあるので、アプリで使用するテーブルとSalesforceと同期するテーブルはきっぱり分離してPostgres内でトリガーで同期するというアプローチはアリだと思いますが、自前のテーブルの方はともかく自動生成される同期テーブルにトリガーをつけて良いかどうかは謎ですね。。。
それにこのやり方で完全双方向は無限ループの回避がやっかいかな?

いずれにせよ使えるようになったら試してみるつもりなのでこの話はまたいずれ。



★ところで flectSalesforce

ちょいちょいあるよー、とはこのブログでも言っていたのですがちゃんと紹介したことはなかったと思うので改めて。

http://oss.flect.co.jp/libs/ja/flectSalesforce.html

flectSalesforceはSalesforceのSOAP APIのJavaフルスクラッチ実装です。

僕は元々某所でSOAPのかなり初期からSOAPクライアント/サーバーを作ったりもしていたのでSOAPには色々と言いたいことがあるんですが、それはまぁ良いでしょう。

とにかくAxisは使いたくなかったので自分で作ることにしたんですが多分正解でした。SalesforceのSOAP実装にも色々と突っ込み所があるんですが、これまた誰の共感も得られそうにないので墓まで持っていきます。(^^;

□□□□
前置きが長くなりましたが。。。
そんなこんなでこれはSalesforce APIの実装のひとつです。必要なものから作っているので全部のメソッドを実装しているわけではありませんが、オブジェクトの更新/削除など主要なものは網羅しています。(describeLayoutやdescribeTabsみたいなメソッドを実装しても誰も使うことないでしょうしね)

元々は社内で使うことしか想定していなかったのでコメントも日本語です。(^^;
(必要最低限しか書いてませんが)

既存のAPIラッパーとの相違点としては単純なAPIのラッパーに留まらず、独自のユーティリティインターフェースを備えている点があげられると思います。

  • SQLライクなINSERT, UPDATE, DELETE構文によるデータ更新
  • Fixtureによるテストデータの更新、削除
  • RDBからのSELECT結果をSalesforceに取り込み
  • SalesforceからのSELECT結果をRDBに取り込み


などなど。
後ろの二つがここから紹介する内容です。
APIの使い方自体は

http://flect-salesforce-sample.herokuapp.com/

でサンプル付きで説明しているのでここでは主に処理の枠組みについて説明します。


★RDBからのSELECT結果をSalesforceに取り込み

http://flect-salesforce-sample.herokuapp.com/sqlsync

RDBからSELECTした結果でCSVを作り、それをBulk APIで投入します。

特徴的なのはSELECTしたフィールドとSaleforceオブジェクトのフィールドの対応をSELECT文内のエイリアスで指定するという点です。

 

SELECT COL1 as Col1__c,
       COL2 as Col2__c
  FROM TABLE1
 WHERE ...

 

この仕様によりJoinや関数の使用などDBMSのサポートするすべてのSQL構文を駆使してSELECT文を書くことができます。

同期処理として運用する場合はWHERE句に更新日付で結果を絞る条件を付加して定期的に実行します。(それに特化したアプリもあります。)
項目に外部IDがあればレコードの更新方法はUPSERTになります。


ちなみにBulkAPIには1万行制限がありますがSELECT結果が1万行を超える場合は内部的にファイル分割されるので、1万行を越えても問題なくデータを取り込むことができます。


★ SalesforceからのSELECT結果をRDBに取り込み

http://flect-salesforce-sample.herokuapp.com/sobjectsync

これもBulkでやりたかったんですが、クエリ自体はSOAP APIで投げています。
何故ならBulkクエリでは参照フィールド(CreatedBy.Nameとか)が取れないからです。。。


使えねー。。。(--

サブクエリは仕方がないと思うけど、参照フィールドくらいはBulkでも取らせて欲しいよ。。。

□□□□

仕組みとしては要するにSalesforceに対してクエリを実行した結果をぐるぐる回しながら指定のキーでUPDATE文を実行し、結果が0件だったらINSERT文を実行しているだけです。
QueryMoreは内部的に処理されるので、結果セットが大きい場合も問題なく実行できます。

こちらの方ではSalesforceオブジェクトのフィールドとRDBのテーブルカラムを一つずつ指定する形式をとっています。

なのでSOQLの関数は使用できません。(あるんだっけ?(^^;;; )

その代わりSELECTした結果をJavaで加工した結果をマッピングすることができます。

 

request.addFunctionMapping("NAME", new SObjectSyncRequest.Function() {
    @Override
    public Object evaluate(SObject obj) {
        String fn = obj.getString("FirstName__c");
        String ln = obj.getString("LastName__c");
        return ln + fn;
    }
});

 

引数のSObjectから値を取得してそれをごにょごにょと演算をした結果をreturnすればOK。データ移行の自由度はむしろこっちの方が高いです。


実行結果の詳細なハンドリングを行いたい場合はListenerクラスを組み込むことで個別にエラーとなったオブジェクトをハンドルすることができます。

□□□□
こんな感じで雰囲気は伝わったでしょうか?
Heroku Connectが完全自動で固定的なのに対して、こちらの方は自由度高くプログラムに組み込めるのが特徴と言えると思います。

実際のところ、これらの機能は社内要件から実装したものであって公開して誰でも使えるようにしているのは、もののついでという感はあるのですが。。。(^^;

ご意見、ご要望等あればGithubのIssue、Twitter等に書いてくれれば可能な範囲で対応するので、定常的な同期に限らずワンショットのデータ移行の場合などでも思いだしたら使ってみてくだされ。(^^)/

2013年12月 6日 (金)

Heroku Platform APIのJavaラッパー

しばらく別の仕事をしていたため一ヵ月以上放置していたんですが、Heroku Platform APIのJavaラッパーをリリースしました。

 

http://oss.flect.co.jp/libs/ja/heroku-platform-api.html

 

Platform API自体がまだベータでどのように変更されるかわからないのでバージョンは0.9としています。(というかいくつかFeatureRequestしようと思ってます。)

元々は全メソッドを実装するつもりはなかったんですが、7割ぐらい実装した段階で全部作ろうと決心しました。でも、どう考えても使うことのなさそうなメソッドも多いです。(^^;

使い方は多分PlatformAPIのJavaDocとHerokuのDevCenterのリファレンスを並べて眺めればだいたいわかるんではないかと。

- http://oss.flect.co.jp/apidocs/heroku-platform-api/jp/co/flect/heroku/platformapi/PlatformApi.html

- https://devcenter.heroku.com/articles/platform-api-reference

基本的にはRESTのAPIとJSONを忠実にJavaに落としていっただけです。ちなみにJavaDocは全クラス、メソッドがGitHubのソースへのリンク付きという親切仕様です。(^^;

以下、いくつかポイントを絞ってわかりにくそうなところと突っ込みどころを解説していきます。

 

★認証

APIを使用するための認証方法には以下の4つがあります。(括弧内は対応するPlatformApiクラスのメソッド名)

  • OAuth(fromOAuth)
  • refresh_tokenをaccess_tokenと交換する方法(fromRefreshToken)
  • ユーザー名とパスワードをaccess_tokenと交換する方法(fromPassword)
  • ユーザー名とAPIキーを使用する方法(fromApiToken)

一番基本的な使い方はOAuthでの認証です。OAuthを使用するためにはあらかじめHerokuダッシュボードのAccount画面でOAuthClientを登録してClientIDとClientSecretを取得する必要があります。

このJavaラッパーではPlatformApi#getOAuthUrlメソッドでHerokuの認可画面のURLがとれるので、そこにブラウザでアクセスしてリダイレクトで返ってきたcodeをPaltformApi#fromOAuthに渡せば有効なPlatformApiクラスが取得できます。

OAuth認証のレスポンスには常にrefresh_tokenが含まれています。つまり一度認可を受けてしまえばあとはずっとオフラインアクセスが可能です。

refresh_tokenはユーザがダッシュボードでそのアプリをRevokeしないかぎりずっと有効です。

あとはユーザ名とパスワードまたはAPIKeyを使用してAPIを叩くこともできます。(APIKeyはコマンドで「heroku auth:token」を叩くと表示されます。)

パスワードでの認証はaccess_tokenとの交換が必要なのに対しAPIKeyは交換なしでいきなり使用できます

これが可能なのでPlatform APIは実は全部以下のようにcurlで1コマンドで実行可能です。

 

curl -i -H "Accept: application/vnd.heroku+json; version=3"
    -u <USER>:`heroku auth:token`
    https://api.heroku.com/apps

 

★Scope

OAuthで認可を求める際に指定できるScopeは以下です。

  • global(全権限)
  • identity(アカウント情報のみ)
  • read(アプリの環境変数以外を読める)
  • write(アプリの環境変数以外を書ける)
  • read-protected(アプリの環境変数も読める)
  • write-protected(アプリの環境変数も読める)

アプリの環境変数が見えてしまうとDATABASE_URLやその他のAddonの設定情報も丸見えなのでデータベースの中身まで見ることが可能になるわけです。

そのためにread/read-protectedのような分け方がされているんだと思うんですが、全アプリに対して同じスコープが適用されるのでほとんど有効なバリアとはなりません。

現状、アプリ毎に個別に権限が指定できないのがPlatform APIの一番の問題点だと思います。

ちなみにglobalスコープでも実行できないメソッドもいくつかあります。(OAuthClientやAuthorizationなど。これらのメソッドを実行するにはパスワードかAPIKeyでの認証が必要。。。なんですけどそもそも使い道が思いつかないですね)

 

★Formation

とはProcfileで定義するWebプロセスやWorkerプロセスのことです。(前は「Process」と呼ばれてた気がするけどこれから変わるのか???)

heroku psコマンド相当のことはFormationまたはDyno関連のメソッドでだいたいできます。

たぶんこの辺が今のところ一番実用的なメソッドです。

 

★ LogDrain

LogDrainはDynoのログを外部のSyslogに流す仕組みです。PapertrailやTreasureDataなどのログを扱うAddonはこの仕組みを利用しています。

一覧を取得するとそこにはAddonが使用しているURLも含まれているので、それを別のアプリでaddすると複数のアプリから一つのAddonに対してログを流すことが可能です。

やっていいのかどうかはわかりませんが。。。(^^;;;

でも多分Addon側からはログを流してきたDynoがどのアプリに属しているかを知る術はないと思うんですよね。。。

 

★LogSession

作成するとhttpsのURLが生成されて、そこにGETでアクセスすると直近のログが取得できます。URLは5分間または接続してから1時間有効です。(とドキュメントに書いてあります。)

接続してから1時間???

どういう意味かと思ってたら作成時のオプションで「tail=true」と指定すると、レスポンスはContent-Lengthなしで繋ぎっぱなしになります。

なんつー乱暴な。。。(--

しかし実際にtailっぽく動作するので便利は便利です。。。でもアリなのか?これ?

 

□□□□

その他、releasesの一覧は取れてもrollbackはできないとか、全体的に突っ込みどころ満載な感じはありますがFeatureRequestやバグレポも割とちゃんと聞いてくれて実際に修正されたりもしているので、皆もっとガシガシ突っ込んであげると良いと思います。(^^;

2013年12月 4日 (水)

ApexからGoogle OAuthを使う

前回艱難辛苦の果てにApexからService Account認証でGoogle APIを使用することに成功したわけですが、Google APIのラッパーとしてService Account認証にしか対応していないのはどうかと思うので、通常のServer Flowにも対応することにします。

と言ってもVisualForceは昨日Hello worldだけは作れるようになりましたが、コントローラのコードの呼び出し方とか意味がわからないのであきらめました。なので作ったのは例によってApexクラスのAPIだけです。(^^;

 

★ Google Cloud Consoleの設定

Server Flowでの認証を行うためにはGoogle側にRedirectURIの設定が必要です。
アプリケーション自体は前回作成したものを使用すれば良いので、追加で必要な設定は

APIs & auth > Registered apps > アプリケーション名 > OAuth 2.0 Client ID

と展開してWEB ORIGINとREDIRECT URIを設定することだけです。

OAuth認証の受け口はVisualForceの画面になる(多分)ので、以下のような感じで自分の組織のVisualForceページのURLを指定します。

  • WEB ORIGIN: https://c.na14.visual.force.com
  • REDIRECT URI: https://c.na14.visual.force.com/apex/GoogleLogin

 

★GoogleOAuthクラスの使い方

ドキュメントとソースコードは以下です。

 

やってることはドキュメントに書かれていることを素直にコードに落としているだけなので見ればすぐにわかると思います。
必須項目はコンストラクタで引数として渡すようにし、それ以外の項目は任意のプロパティとしています。(access_typeなど2値のプロパティはBoolean型のプロパティとしています。)

それぞれの項目の意味は以下。

client_id:
Google Cloud Consoleで作成したアプリケーションのCLIENT ID

secret:  
Google Cloud Consoleで作成したアプリケーションのCLIENT SECRET

scope:
認可を求める機能のURI。空白区切りで複数設定可。
カレンダーの読み書きを実行したい場合はGoogleCalendarServiceで定数として定義されているSCOPE_READWRITEまたはSCOPE_READONLYを指定すれば良い。

redirect_uri:
Google Cloud Consoleで設定したREDIRECT URI
ここで指定したURLにVisualForceでcodeとaccess_tokenを交換するロジック(GoogleOAuth#authenticateメソッドの実行)を実装する。

state:
任意の文字列。
指定するとredirect_uriで指定したURLに戻ってくる時にクエリストリングとして「&state=xxxx」のように自分が設定した値がそのまま返ってくる。
例えばログイン後に表示したい画面を切り替えるなど開発者側で自由に意味を持たせることができる。

offline_access: (access_type=offline)
offline_accessを使用するかどうか。
offline_accessを有効にした場合、認証のレスポンスにrefresh_tokenが含まれるのでそれを利用してaccess_tokenの有効期限が切れた後でも、いつでもどこからでも何度でもAPIを実行可能になる。

force_approval_prompt: (approval_prompt=force)
Googleの認可確認画面を毎回表示するかどうか。
デフォルトでは表示しない(approval_prompt=auto)となっていて、すでに認可がある場合は確認画面をスキップする

login_hint:
ログインユーザーに関するヒント。
アプリ側からログインユーザーを指定するなどの使い方ができるらしい

□□□□
これらの項目を設定すればGoogleOAuth#getLoginUrlメソッドでGoogleの認証URLが返ってきます。
そのページでアプリを承認するとredirect_uriで指定したURLにクエリストリングでcodeが返ってくるので、それをGoogleOAuth#authenticateメソッドに渡せばaccess_tokenが取得できます。あとは取得したaccess_tokenをGoogleCalendarService#setAccessTokenメソッドに渡せばCalendarAPIを使用できるようになります。(READMEのサンプルがほぼ全てです。)

https://github.com/shunjikonishi/apex-google-api

 

★offline_accessについて

offline_accessを要求して取得したrefresh_tokenはユーザがそのアプリをRevokeするまで自由に何度でも使用することができます。(このライブラリではGoogleOAuth#refreshTokenメソッドにrefresh_tokenを渡すことでrefresh_tokenとaccess_tokenを交換します。)

最初に一度だけrefresh_tokenを取得しておけば、それを延々と使い続けることができるので実質的にはService Account認証の代替として使用することが可能です。

offline_accessにはもう一つService Account認証よりも優位な点があってそれはメールアドレスとして自分の好きなアドレスを使用できるという点です。

Service Account認証ではイベントの作成者欄に表示されたり、招待メールのFromに設定されるメールアドレスはGoogleが自動発行したアドレスになるんですが、このアドレスが「999999999999-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@developer.gserviceaccount.com」(文字数を実際のアドレスと揃えてみました。)みたいな形式だったりするので誰だよ!っとなることは目に見えています。(JWTでprnを指定することで変更できるようですが、権限の設定方法がよくわからず。。。というか機能していない気がします。(--)

この点offline_accessを使用する場合は自分で用意したアドレスを使用できるので、怪しさは軽減されます。(^^;

。。。
どうりでService Account認証に関する資料やブログが少ないわけだよ。。。
誰も使ってないんでしょうね!

2013年11月29日 (金)

ApexからGoogle Calendarを操作する(完結編)

前回の続きです。

前回なんとなく動くCalendar APIを作ることはできたわけですが今回はそれを使うためのセットアップ方法と使い方を解説します。

 

★ インストール

ソースコードはこれです。

https://github.com/shunjikonishi/apex-google-api

Force.com IDEに取り込むなどして、使用する環境に組み込んでください。

 

★ Google Cloud Consoleの設定

Service Account認証を行うためにはGoogle側にアプリケーション登録が必要です。
アプリケーションの設定はGoogle Cloud Consoleで行います。

https://cloud.google.com/console

必要な手順を列挙すると

  •  連携用のプロジェクトを作成する
  •  画面左のメニューから「API & auth > APIs」と展開し、Calendar APIをONにする
  •  「API & auth > Registered apps」の画面で新しい「Web application」を作成する
  •  上で作成したApplicationの画面で新しいCertificateを生成する


という感じになります。
スナップショットが見たい場合はここが比較的新しいかな?

http://d.hatena.ne.jp/shimooka/20130912/1378980591

作業を行うにあたり参考にしたサイトはいくつかありますが、GoogleのUIはころころ変わるためかなり高い確率で「そんな画面ねー!」とか「そんな選択肢ねー!」という説明に遭遇します。
ここに書いた手順も光の速さで陳腐化すると思うので手順よりも枠組みを理解してください。

Certificateを生成するとアプリ用のメールアドレスが発行され、PrivateKeyがp12ファイルとしてダウンロードできます。
PrivateKeyのダウンロードはこの時に一度しか行えず後から再ダウンロードはできないので注意してください。(まぁCertificateの生成をやり直せば良いんですが。)

あとファイルから鍵を取り出すためのパスワードもダウンロードダイアログにさらっと表示されるだけです。固定で毎回同じだと思うので別に良いんですが、最初気がつかなくて結局これも後から検索して調べました。(^^;

手順を鵜呑みにせずに、ちゃんと途中に出てくるメッセージは読みましょう。

 

★ Sign Serverを建てる

前回も書きましたが、現在のApexではSHA256での署名ができません。
そのため外部に電子署名のためのサーバを建てる必要があります。

https://github.com/shunjikonishi/sign-server

これを動かすのはやはりHerokuが一番お手軽でセットアップ方法はREADMEに書いてある通りです。

環境変数「PRIVATE_KEY」には先にダウンロードしたp12ファイルを丸ごとBASE64エンコードした値を設定すればOKで、ファイルのBASE64エンコードには僕はopensslを使用しました。(改行コードは自分で取り除きました。)

 

openssl enc -base64 -in xxxx-privatekey.p12 -out temp.txt

 

セキュリティを高めたい場合はBasic認証とIP制限を設定することができます。このサーバはSalesforce以外からアクセスできる必要はないのでALLOWED_IPにSalesforceのIP範囲を設定しておくことをお勧めします。

HerokuのDynoをスリープさせないためのPOLLING_URLの設定は必ずしも必要ありませんが、Salesforceのコールアウトの設定はかなり短く設定されているのでDynoがお休み中だとそこそこ高い確率でDynoが起きるよりも先にタイムアウトエラーになります。(^^;

もちろん電子署名のために必ずこのSignServerを使用しなければならないとか、Herokuを使わなければならないということはありません。

電子署名のために必要なコードはこの程度なので、適当なサーバがあるのであればそちらに組み込むことも可能です。

 

★コーディング

ここまで来たら後は実際にコードを書くだけです。
まずは認証。

 

SignServer sign = new SignServer('https://YOUR-APPNAME.herokuapp.com/sign');
service = new GoogleCalendarService(sign);
JWT jwt = new JWT('YOUR-APPACOUNT@developer.gserviceaccount.com');
service.authenticate(jwt);

 

SignServerのコンストラクタには作成した署名サーバのPOST URLを渡します。
JWTのコンストラクタ引数はGoogle Cloud Consoleで証明書を生成した時に同時に生成されるメールアドレスです。

JWTではscopeやセッションライフタイム等を自分で設定することもできますが、未設定の場合はサービスクラスがよしなに設定してくれるのでメールアドレスのみの指定で問題ありません。

ちなみに既存ユーザのカレンダーを読み書きする場合は、このメールアドレスに対して共有設定を行います。

あとはGoogle提供のAPIリファレンスを見ながら必要なメソッドを実行してください。(※全部のメソッドを実装している訳ではありません。)

https://developers.google.com/google-apps/calendar/v3/reference

このAPIリファレンスはとても見やすいですし、APIとモデルもこのリファレンスに忠実に作っているので特に使い方で迷うところはないと思います。

ここでは知らないとわからないGoogle Calendar固有のトピックを紹介します。

 

- 時間の設定方法

各イベントの日時の指定はJSON上は以下のようになっています。

 

"start": {
    "date": [date],
    "dateTime": [datetime],
    "timeZone": [string]
},
"end": {
    "date": [date],
    "dateTime": [datetime],
    "timeZone": [string]
},

 

 開始、終了時刻を別々に指定するわけです。
dateとdatetimeの両方がありますが、使用するのはどちらか一方のみです。timeZoneは省略時にはカレンダーのデフォルト設定が使用されます。

終日イベントを作成する場合は以下のようにendにstart + 1のdateを指定します。

 

"start": {
    "date": '2013-11-28',
},
"end": {
    "date": '2013-11-29',
},

 

 ところでGoogleCalendarEventクラスではstart, end, date, dateTimeなどいくつかのプロパティがプロパティ名に「_xx」を付加した形で定義されています。
これは「end」や「date」などいくつかの単語がApex内の予約語や組み込みクラス名とかぶるために使用できなかったために取った迂回処置です。(「start」は予約語ではありませんがバランスを考えて「_xx」を付けました)

なので、日時をプロパティで設定しようとすると「event.start_xx.date_xx = d」のようになるんですが、それもいけてないんでいくつか定義されている日付設定用のメソッドを使うようにしてください。

 

Datetime d1 = Datetime.newInstance(2013, 11, 28, 15, 0, 0);
Datetime d2 = Datetime.newInstance(2013, 11, 28, 16, 30, 0);
//コンストラクタ
GoogleCalendarEvent e1 = new GoogleCalendarEvent('イベント名', d1, d2);

//開始終了日時を別々に指定
e1.setStartDatetime(d1);
e1.setEndDatetime(d2);

//開始日時と期間(分単位)で指定
e1.setDateAndDuration(d1, 90);

//終日
e1.setAllday(d1.date());

 

 - CalendarとCalendarList

CalendarListとはGoogleカレンダーのWeb画面の左端に表示されるカレンダー一覧の一個のインスタンスのことです。名前がまぎらわしいですがカレンダー一覧全体を指すものではありません。

CalendarListは名前やタイムゾーンなどのCalendarの持つすべてのプロパティを内包しますが、色や非表示設定などの一覧表示のために必要なプロパティはCalendarListにのみ存在します。
Calendarにはlistメソッドがないので使用可能なCalendarのリストを取得するためにはCalendarListのlistメソッドを使用します。

この実装ではGoogleCalendarListはGoogleCalendarを継承しているのでGoogleCalendarService#listCalendarListメソッドで取得したGooglCalendarListクラスはそのままGoogleCalendarとして使用できます。

※現在CalendarListの作成、更新、削除メソッドは実装されていません。

 

- updateとpatch

カレンダーやイベントの更新メソッドにはupdateとpatchの二つがあります。

updateメソッドはupdateというよりもむしろreplaceです。Bodyに指定された内容で既存のインスタンスを丸ごと置き換えるイメージ。

一方のpatchメソッドはまさにpatchで既存のインスタンスの上に指定された項目のみを上書きするイメージです。

実際のところはupdateメソッドに渡すインスタンスはIDを含む完全な内容でなければならないので、getで取得したインスタンスを加工したものしか渡すことができません。逆にpatchメソッドの場合はIDを含まない一部の内容を渡すのでgetで取得したインスタンスを渡すことはできません。

ちなみにpatchメソッドにattendees(ゲスト)のようなList項目を設定した場合、既存のリストにアイテムが追加されるのではなく、リスト全体が置き換わります。
このため既存のイベントに対してゲストを追加する場合のコードは以下のようになります。

 

//ゲストを一人招待した状態で新規イベントを追加
GoogleCalendarEvent e1 = new GoogleCalendarEvent('カレンダーテスト', Date.newInstance(2013, 11, 30));
e1.addAttendee('xxxx@flect.co.jp');
e1 = service.insertEvent(cal, e1);
System.debug('Inserted Event: ' + e1);

//ゲストをもう一人追加する
GoogleCalendarEvent e2 = new GoogleCalendarEvent();
e2.attendees = e1.attendees;//元のゲスト一覧をコピー
e2.addAttendee('yyyy@flect.co.jp');
e2 = service.patchEvent(cal, e1.id, e2);
System.debug('Patched Event: ' + e2);

 

 使用されているHTTPメソッドがそれぞれPUTとPATCHであるところがRestfulな感じです。(^^;

 

★今後の予定など

カレンダー関連の現在未実装のメソッドは必要に迫られれば実装しますが、あんまり積極的に実装しようという意気込みはありません。
実際のところこの辺の実装はもはや単なる作業でしかないので、むしろ必要な人が自力で実装してPullReqください。(^^;

認証関連はJWTを使ったService Account認証しかできないのはあんまりなんで、通常のOAuth Server Flowは実装しようかと思ってます。(いつ?)

Spreadsheetなどのその他のGoogle APIについては簡単に実装できそうならやっても良いかなぁと思ったんですが、今見たらSpreadsheetはV3 APIもGDataベースのようなんであんまり関わりたくありません

ていうかApexはもうしばらくいいです。。。(--

-------

2013/11/30 追記
@stomitaさんのご指摘からSignServerにBasic認証とIP制限の設定を追加しました。
@stomitaさん、ありがとうございます。

2013年11月28日 (木)

続・ApexからGoogle Calendarを操作する

どもども。先週の記事「ApexからGoogle Calendarを操作する」の続きです。

といっても厳密には続いてないんですが。。。(^^;

 

★Google Data API Toolkitのお話

さて、先週の記事ではぶつぶつ文句を言いながらも、「Toolkitで一通りカレンダーいじれるんじゃないの?」という結論に達しました。

しかし、今週になってやっぱり使うのを止めました。(--

イベント作成時に使用するオブジェクトが何故かSalesforceのEventオブジェクトでなんかビミョーという話は前回さんざんしましたが、実際のところはタイトル、日時、場所、説明さえ設定できれば良かったのでそれで事足りるはずだったんです。

なのに、実際に試してみると何故か場所が設定されない。。。

あれ、なんでだ?自分のコードではちゃんとEvent#Locationに値を設定してるし。。。

。。。

。。。て、CalendarService#insertEventsで引数に渡したLocationがコピーされてねー

そらあかんわ。。。(--

 

□□□□

さて、どうしたものか?

直接修正するなら1行で直せる程度のものですが、それも気が進まない。

とりあえず元のリポジトリを探してみたところ。。。

https://code.google.com/p/apex-google-data/

うーん、もう3年近くメンテされた形跡が無い。。。

その間にAPIもXMLベースのV2からRESTベースのV3に変わっているわけで、これはもうこの先も放置なんだろうなぁ。。。(--

 

★V3 APIの使用を検討してみる

さてさて、ソースまるごとフォークして自分でメンテすることも考えましたが、それやろうとすると直したいところが多すぎる。(--

それに今は面倒なXMLを使わなくてもお手軽にRESTで実行できるV3 APIがあるんだよ!

ということでとりあえずV3のドキュメントを読んでみることにしました。

https://developers.google.com/google-apps/calendar/

うん、やっぱりReference見ただけでもV2よりもはるかに簡単に使えそう。それにDeprecation Policy見ると旧APIは2015年4月までは使えるけど、そっから先は知らん!と書いてるようにも思えるしこれはやっぱりどうでもV3を使うべきでしょう。

 

★Service Account認証

とりあえずApex用のV3ライブラリを探してみるけど、どうも無いっぽい。まぁ「Salesforce Google API」みたいなワードで検索すると一撃でSalesforce公式(?)っぽいToolkitのページが見つかるからその先に踏み込む人もあんまりいないんだろう。。。2015年4月以降をどうやって乗り切るつもりかは知らないけど。。。(--

V3 APIをざっと眺めるかぎり少なくともAPI本体のREST部分はものすごくシンプルなので自分で作るとしてもそんなにたいしたことはなさそう。

問題は認証部分。

V3 APIではClientLoginは廃止されたので今までのように気軽に個人のユーザーアカウントとパスワードでログインしてAPIをたたくことはできない。

そうはいってもサーバーアプリの場合ユーザーとは無関係の固定のアカウントでAPIを叩きたい場合も多い訳で、そういう場合はService Account認証というのを使うらしい。

https://developers.google.com/accounts/docs/OAuth2ServiceAccount

これは連携用のアプリケーションを登録して、そこで発行されたClientIDとPrivateKeyを使用して認証するという方法らしい。

下に詳細な実行手順が書かれているけど要するに

  • JSONで認証用の文字列(JWTというらしい)を組み立てて、
  • Base64でエンコードして、
  • 電子署名を追加して、
  • Http POSTで投げる

とすれば良いらしい。(どうでもいいんだけどこれもOAuth2の一種なの?一般的なOAuthとまったく類似点が無い気がするんだけど。。。)

ドキュメント読んでると3回位「悪いことは言わないから自分で実装したりせずに既存のライブラリ使っとけ!」と、言われるんだけどApex用のライブラリは無いんだよ。。。(--

ApexにもJSON、EncodingUtil、Crypto、と必要な道具立ては揃ってるっぽいから多分作れるんじゃないかな?

。。。とりあえず作ってみるか!

 

★で、作ってみた

けど無理でした。。。(--

認証文字列を作るところは良いんだけど最後の最後で署名が。。。(--

ここではSHA256withRSAで電子署名する必要があるんだけど、ApexのCrypto#signではSHA1withRSAでしか署名できないという罠。。。(--

マジでかー(--

ここさえ突破すれば終わりなのにこれはどうしようもねーよ。。。

どうしよう。。。

。。。しょうがないから、外に署名だけするサーバー建てるか。。。

 

★まさかのHeroku登場

作りました。

https://github.com/shunjikonishi/sign-server

ホントにPOSTされた文字列に電子署名して返すだけのアプリ。HerokuにpushしてConfigにBase64エンコードした秘密鍵を設定すれば使えます。

。。。(^^;;;

なんたるリソースの無駄遣い!数年前にはここまで単機能のサーバを気軽に作る日が来るとは思いもしなかったよ。

さすがにちょっとやりすぎなんじゃないの?という気がしなくもないけど、このアリサマはSalesforceがSHA256をサポートしていないのが原因なんでまぁいいか。(^^;

ということで、一応動くGoogle Calendar API V3のラッパーを作ることができました。

https://github.com/shunjikonishi/apex-google-api

次回はこいつの使い方を解説します。(^^v

 

□□□□

蛇足

実はSalesforceにもJWTを使った認証があるということを今回調べてて知りました。

SalesforceのAPIはIDとPassword(とSecurityToken)の認証で普通に使えるからこれ使う人いるのか?という疑問はまぁおいとくとしてもそこではSHA256での検証もやってるっぽい。

そこまでやってるなら後はそれをCryptoにもぶちこめば良いだけなんでとっととやって欲しい。。。(--

たったそれだけでGoogle V3 APIが自由に使えるかどうかが変わってくるんだからホント頼むよ。。。m(_ _)m

2013年11月20日 (水)

Dreamforceには行ってない

まさかの3連投。(^^;

DreamforceでのHeroku関連のアナウンスはこれみたいですね。

https://blog.heroku.com/archives/2013/11/19/tools-for-integrating-heroku-apps-with-salesforce

FLECTからは役員2人が行っているので、お土産話に期待したいところです。

Heroku1とはいったい何なのか?サイト見ても人柱募集中!であることしかわからないよ。。。(^^;

★ Force.com CLI

SalesforceのWeb画面にいちいちログインするのがめんどくさいので、とりあえずコマンドラインツールを作ったそうです。
つねづねかねがね思ってたけどHerokuのエンジニアは自分と精神構造が近い気がするよ。。。(^^;

psqlのようなシェルに入ってコマンドを叩くタイプではなく、herokuコマンドのようにログインアカウントを最初に覚えさせて毎回「force xxxx」としてコマンドを実行するタイプのようです。

実装は例によってRubyだろうと思ってたら、なんとGoogle Go。
僕は公開されているソースはとりあえず眺める習慣があるんですが、最近はGoで書かれているツールも多いような気がします。やっぱりexeにできるのが大きいんですかね。そろそろ真面目に学習してみようかな。。。

★ Force.com Client Libraries

RubyとNode.js用のSalesforceクライアントライブラリを作ったそうです。
これって今までなかったんだっけ???

HerokuではRubyとNodeがトッププライオリティの言語と言うのは前にも聞いたことがあったんですが、やっぱりこの2つは特別扱いなんですかね。

ちなみにJavaのクライアントライブラリは自前で書いてます。(^^v

http://oss.flect.co.jp/libs/ja/flectSalesforce.html

★ Introducing Heroku Connect

SalesforceのデータをHeroku Postgresと同期するツールを絶賛開発中だそうです。

。。。

ていうか、これもついこの間自前で書いたよ。。。(--

http://flect-salesforce-sample.herokuapp.com/sobjectsync

元々は案件要請から作ったものなんだけど数行のコードでSELECT結果をRDBに取り込めて間に関数もはさめるので実用性はかなり高いと思います。更新日時を取っておいてスケジューラで起動すればそれだけで同期ツールにもなるし。

もうちょっと内容整理して、アプリの形にしてからブログに書こうと思ってたけどHeroku謹製のモノができるのであればもういらないかな?

やはり人柱に応募すべきなのか。。。(--

★ Come see us at Dreamforce

行けません。(--

2013年11月19日 (火)

ApexからGoogle Calendarを操作する

こんばんは。およそ1年半ぶりにApexコードを書いている小西です。(--

さて今回のお題はSalesforceからGoogleカレンダーのイベントを読んだり書いたりするやり方の調査です。

「apex google calendar」でググると一発でGoogle Data APIのドキュメントが引っ掛かるので楽勝です。

http://wiki.developerforce.com/page/Google_Calendar_API

ただし、、、Apexに詳しい人ならな。。。(--
文法とかDeployの仕方とかもう忘れたよ!!!

★ Google Data APIのセットアップ

さて、文句ばかり言っていても仕方がないのでとっととやっつけてしまいましょう。
まずはGoogle Data APIのセットアップ方法はこちら。

http://wiki.developerforce.com/page/Google_Data_APIs_Toolkit_Setup

詳細な手順は省略しますが、やっている内容は以下です。

  • Force.com IDEのインストール
  • Subclipse(EclipseのSubversion プラグイン)のインストール
  • Googleのリポジトリからgoogle_data_toolkitをチェックアウト
  • 自分のSalesforceアカウントにgoogle_data_toolkitを取り込み
  • Salesforceのリモートサイトの設定に「https://www.google.com」を追加


要するに必要なApexクラスをインポートしているだけです。
僕は今回 Force.com IDEのインストールからやっていて、純正Force.com IDEではプラグイン取得がメニューから消されてるとか、何故か管理者権限で動かさないとForce.com IDEとSalesforceの同期がエラーになるとかに軽くひっかかりましたが、多分これを読んでいる人は皆僕よりもSalesforceに詳しいのでその辺もスルーで良いでしょう。

★ Calendar APIを使ってみる

さて、環境がととのったらあとは実際にAPIを叩くだけです。
最初に示したドキュメントにはコードサンプルもそれなりに載っているので楽勝です。

今回の場合、特定のユーザでいくつかのカレンダーにアクセスできれば良いので単純にID/Passwordでログインしたかったんですが。。。あれ???

ドキュメントにはOAuthで認証する方法しか載っていません。。。(--

□□□□

さらに検索をかけるよりも、もはやソースを見た方が早そうだったのでざっとソースを眺めることにしました。

JavaのGoogle Data APIは単純にXMLの各要素をメソッドに対応させただけのシロモノでメチャメチャ冗長で扱いにくかった記憶があるんですが、Apex版はクラス数も少なくてかなり簡略化されている印象があります。(Java版も今は変わっているかもしれませんが。)

実際、Calendar APIを使用するだけなら読まなければならないクラスは

  • CalendarService
  • GoogleData


の2つだけです。ただし、この2つは必ず上から下までざっとでも眺めた方が良いです。


★ Google Data APIのソースを読むべき理由

まぁ僕もそれほどしっかりと読んだわけではないんですが、それでも2つばかり「なんだかなぁ。。。」と思った部分があるので以下にそれを記載します。


- Eventオブジェクトはカレンダーイベントのラッパーではない

ドキュメント見た時から不思議だったんですが、Calendar APIではInsert時にはEventオブジェクトを引数として渡します。しかし、何故かUpdate時にはEventオブジェクトではなくDOMのElementが引数になります。EventオブジェクトがイベントのラッパーであるならUpdateもそれを使えば良いじゃんと思うんですが、実はEventがあらわすものはGoogleカレンダーのイベントではありません。Salesforceの組み込みオブジェクトのEventです。
よく考えると名前が被るのでEventという名前のApexクラスは作れないんですが、なんとも中途半端な実装をしたものです。。。(--

このおかげでUpdate時には(頑張れば)どのような項目でも設定できますが、Insert時には設定することのできない項目があります。

ちなみにここで言う「頑張る」とは自力でDOMのElementを足したり引いたりすることなので、そんなことする位なら多少パフォーマンス落としても削除してからInsertしなおすよ!と思っちゃいます。。。(--



- ソースの中にちらほらとToDoが残っている

例えばこんなのとか

    // TODO support for recurring events

「繰り返し項目をサポートする」。SalesforceのEventオブジェクトにも繰り返しの設定らしきものはあります。それがGoogleカレンダーの繰り返しときれいに対応する形式なのであればすぐに作れたろうと思うんですが、多分しなかったんでしょうね。。。(--
「ラッパーあるから楽勝!」と思ってタカをくくっていると痛い目にあいかねません。(自力でDOMをどうにかすればできますけど。。。)


これもEventオブジェクトをそのまま流用したことの弊害だと思います。この仕様はSalesforceのEventをGoogleカレンダーに一方向に流すだけの場合は便利なのかもしれませんが、どうにも設計ミスのように思えます。。。(--

ちなみに当初の目的であったID/PasswordでのログインメソッドはCalendarService#useClientLoginです。

★ 今後の方針とか

さてさて、どうしたものでしょう。。。(--
自前で簡単なラッパークラスを作るだけでも格段に使い勝手はよくなりそうな気はします。でも実際のところは素のまま使ってもたいていの要件は間に合うような気がするんですよね。

必要に迫られたら作る、というのが個人的な方針でもあるので今回はスルーでも良いかなぁ。。。

決してApexのコードを書きたくないとかそういうことではないんですが。。。(^^;

★ Googleからのアカウントブロックメールに注意

そういえば、最後にもうひとつ。ApexからのID/Passwordでのログインをテストしてから次のテストコードを書いて、実行すると何故かログインに失敗するようになりました。

最初は自分のコードのバグかと思ったんですが、どうにもさっきは成功していたコードで失敗しているように見える。。。

なんでだろう?と思っていたらメールボックスにGoogleからの「不正なログインがブロ​ックされました」というメールを発見!

ほぅ。。。どうやらGoogleはログインリクエストのUser-AgentやIPをチェックして、こいつは怪しい!と判断したら即座にそのログインをブロックするようです。

ちなみにこの状態でも日常使用しているブラウザやスマホは問題なく使えていました。(多分)

メールに記載された手順でロック解除するとすぐに使えるようになりましたが、これも知らないと結構ビックリしますよね。(^^;

GitHubにリンクするDocletを作ってみた

こんばんは。

今回タイトルにあることがすべてですが。。。(^^;

ふと思いついてGitHubにリンクするDocletを作ってみました。

StandardDocletから改造したのはたった一箇所、クラス名の横にOctocatをおいてGitHub上のソースにリンクするだけ。

Javadoc

結構便利です。(^^v

仕組み的にはGitHub以外でもJavaのソースが階層構造でアクセスできるサイトなら他でも使えるんですが、リンクアイコンをOctocat埋め込みにしたのでGitHub専用です。

その辺パラメータにしようかとも思ったんですが、あんまりパラメータ増やしたくなかったので追加のパラメータはリンク先のGitHubのURLを指定するだけ。

MavenやAntからももちろん使えるのでOSS界隈で広まってくれるとちょっと嬉しいですね。

2013年11月12日 (火)

Heroku Postgres 2.0がリリース

したらしい。。。ってそれは何ぞ?(^^;

アドオンページ見ると料金体系の名前とかも変わってますね~。

https://postgres.heroku.com/blog/past/2013/11/11/heroku_postgres_20

料金は以前の同列と比較すると若干安くなっているような気がしますが、Ikaを使っていた人が
「あなたの新しいプランはStandard Ikaです!」とか言われると「Premium Ikaに上げないといかんのかな。。。」
という強迫観念に襲われるような気がしないでもない。

ていうか、今まさに思ってる。(^^;

機能的にどこが変わったのかという辺りをざっとみると以下のような感じのようです。

★Operational Expertise built-in
運用に便利な機能内蔵してます。

- 使ってないインデックスあったら教えてやんよ!
- データ量が逼迫して来たら教えてやんよ!
- セキュリティパッチなんかは勝手に当てとくよ!
- ハードウェアトラブルもまかしとけ!

どっちかというと限定的でもいいからConfigをいじらせてくれ、と個人的には思うんですがどうやらその辺はこっちでやるから気にすんなよ!というスタンスのようです。
まぁ多分何かしらのトラブルにぶち当たるまではそっちの方が圧倒的に楽です。

★Rollback
heroku release:rollback相当のことがPostgreSQLでもできるようです。
これは。。。便利かも!

deployのタイミングでテーブルの追加やスキーマ変更を同時に行う必要が出てくるというのは良くある話で、deployを元に戻した時にはその変更も元に戻してくれるらしい。

それは凄い!!!

これってどういう仕組みでやるんだろう???と思ったらここに説明されてました。

https://devcenter.heroku.com/articles/heroku-postgres-rollback

ロールバックポイントというのが各プラン毎に一ヵ月、とか一週間とか設定されていてそこから指定の日時までの更新をREDOログを元に再生しているみたいですね。

リリースの為のDDLを流す時にはその時間を覚えとけ!ってことかな。
最初の期待値高かったけど、でもそれだったらpgbackupでも良いような気がしなくもない。。。(^^;

あれば使うけど。

★High Availability
これこそが各プランのStandardとPremiumの格差のようです。
要するに自動でFollow DBも作っておいてくれて障害時には自動で切り替えてくれるってことなんだと思うけど、ちょっと高いかなぁ。。。

Followを作る動機には障害対応の他にも参照専用のレプリカが欲しい、というものもあってそっちには安いプランを使ってたりするのでちょっと考えどころです。
これを使うとその為のFollowは不要になる、という風には読めないですし。

ていうか、ハードウェアトラブルもまかしとけ!ならそれで十分な気も。

★New Tiers and Pricing
Hobby、Standard、Premiumの違いはなんとなくわかったけどEnterpriseってなんでしょうね?
個別契約かな?


★Availability
PostgreSQL9.3がデフォルトになりましたっと。

2013年10月23日 (水)

SendGrid Nightに行ってきた

こんばんは。

約1ヶ月ぶりのエントリです。サボりすぎだろ!

あいすいません!!!

 

さて、本日はSendGrid Nightに行ってきたのでそのレポートエントリです。

SendGridは数あるHerokuアドオンの中でも個人的には1、2を争う秀逸アドオンなのでこれはかかせないイベントだったんですが予想以上に収穫の大きいイベントになりました。

SendGridと関係ないところもありますが以下トピックです。

 

構造計画研究所さんの本気度合いを見た

@nakansuke氏とは以前から友人付き合いをさせてもらっているので、かなり早い時期からSendGridの代理店をやるという話は聞いていたんですが、一人だしどれくらいのことができるのあかなぁというのは正直半信半疑な部分がありました。しかし今日のイベントではチームメンバーがいきなり5人に増えてました。

5人いれば色んなことができそうだよ!

ドキュメントの日本語化やサポートなど結構期待して良いんじゃないかと思います。

 

実はsendgrid4jが使われている

自前のSendgrid APIラッパーであるsendgrid4jを使っている人がいるということを今日はじめて知りました。(^^;

こんなブログが書かれているということもさっき初めて気がついたよ。。。。

正直なところ自分の作ったものを使ってもらうべく啓蒙するという活動には熱意がない方なんですが、使っているのならGitHubにスターをつけてくれるとちょっと嬉しいです。

今のところこのライブラリは自分の使いそうな部分しか実装していませんが、SendGridのAPIはかなり単純なワンパターンなので他のAPIの実装も単純作業でしかありません。

なので足りないAPIなどもIssueにあげてもらえればたいてい対応できると思います。(もちろんPullRequestも歓迎です。)

 

SPFとかDKIMとか

LTは弊社の若葉を含めて4人

 うち2人は信頼性を向上させるためにSPFとかDKIMを設定しましょうという話でした。

SPF、DKIMという単語は一般的なWeb開発を生業としている人達はまず聞いたことがないと思います。ググればなんとなく概念はわかりますが、多分すぐに忘れるんで設定しておくと受信側でSPAMと判定される確率が減る、ぐらいに理解しておけばOKでしょう。

逆に言うと設定しなくても問題なくメール送信はできるので、英語ドキュメントしかない現状ではスルーしている利用者も多い気がします。この辺は日本語ドキュメントに頑張ってほしいですね。

Parse APIはメール受信のタイミングで任意のURLにPOST通知する機能です。
例えば空メールによる会員登録など、コマンドとしてメールを使う際には重宝しそうな機能ですが、現状ではメールアドレスのローカルパート部分がなんであっても全部同じURLにPOSTされるらしい。。。惜しい!

アドレス毎にPOST先のURLを指定できるともっと便利になると思います。

弊社からの発表はEventAPIとPapertrailを連携させるお話。これは不達メールをハンドルする方法としては圧倒的にお手軽でお勧めコンボなんですが、LTにしてはちょっと発表長すぎ。。。(^^;

これがHeroku Meetupだったら容赦なくA氏にプロジェクタ引っこ抜かれてます。(^^;;;


Herokuの情報発信を待っている人がわりとたくさん

わりとたくさんの人にこのブログまたはスライドを見ていると声をかけられました。

僕にとってHerokuの情報発信はボランティアではなく、完全に業務の一部なんですが最近サボり気味です。m(_ _)m

近日PlatformAPIについてなんか書きます。

 

採用情報

株式会社フレクトでは、事業拡大のため、
Salesforce/Force.comのアプリケーション
開発
HerokuやAWSなどのクラウドプラッ
トフォーム上でのWebアプリケーション開発

エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみた
い方、是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、
セールスフォースパートナーです。
heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。
Powered by Six Apart