前回の続きです。
前回なんとなく動く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さん、ありがとうございます。