tyoshikawa1106のブログ

- Force.com Developer Blog -

Salesforce Hack Challenge 2014に参加したときの話

f:id:tyoshikawa1106:20150120221603p:plain

2014年12月04日から2014年12月14日までの期間で開催されたSalesforce Hack Challenge 2014に参加しました。Salesforce1 Platformの何れかのテクノロジを利用していれば、あらゆるタイプのプログラムでのエントリできるハックチャレンジです。


今回、Force.com Developer募集管理アプリケーションというタイトルでForce.com アプリ部門で参加し、オープンソース賞を頂きました。

f:id:tyoshikawa1106:20150120222600p:plain


Salesforce1 Platformの機能を活用し、かつオープンソースとして公開したことによって、他の開発者が有効なサンプルとして活用可能な点を評価して頂いたみたいです。

f:id:tyoshikawa1106:20150120223742p:plain


こういった開発者のイベントに参加して、評価して頂けたことはとても嬉しく思います。

アプリ概要

Force.com Developerを募集し社内共有、管理を行うアプリケーションです。以下のスキルをつかって開発を行いました。

  • Force.com Sites
  • KnockoutJS
  • Bootstrap SF1
  • Chatter in Apex (Connect API)
  • Visualforce
  • Apex Component
  • Apex Trigger
  • Apex Batch (Schedule Batch)
  • Salesforce1 Mobile
  • Report&Dashboard

今回はForce.comサイトの活用とKnockoutJS、BootstrapSF1をつかったアプリ開発、そしてChatter連携を行うことを意識しました。また、Salesforce開発ではお馴染みの Visualforce、Apex Trigger、Apex BatchとできるかぎりSalesforce Platformを活用した開発を行なうことにしました。Salesforce1 Mobile / レポート&ダッシュボードというように標準機能も取り入れています。

主な機能

ハックチャレンジへ参加する際に想定していたアプリの主な機能は以下のとおりです。

  • 応募者はForce.comサイトの入力フォームから自分の情報を登録
  • 応募者の情報が登録された際に自動でメール返信を行う
  • 新規応募者が登録された際にChatterで社内関係者に通知する
  • Salesforce1アプリで登録された応募者情報を検索・一覧表示できる
  • Salesforce1アプリで登録された応募者情報に対してコメント・評価ができる
  • 応募者の住所情報でどの地域からの応募が多いか集計してグラフ表示を行う
  • 応募者がどこで求人情報を知ったのか集計してグラフ表示を行う

アプリの紹介

Force.comサイト入力フォーム

応募者はForce.comサイトで用意した入力フォームに必要事項を入力します。

f:id:tyoshikawa1106:20150122000558p:plain

https://ty-hack2014-developer-edition.ap0.force.com/


画面下の登録ボタンをクリックすると登録処理が実行されます。今回登録処理はApexClassを使わずRemoteObjectsを使って対応してみました。

f:id:tyoshikawa1106:20150122001333p:plain


次のメッセージが表示されたら登録完了です。

f:id:tyoshikawa1106:20150122001755p:plain


登録された情報は応募者オブジェクトで参照できます。

f:id:tyoshikawa1106:20150122002249p:plain

応募者登録時のメール通知

応募者が登録されたとき、Apexトリガーで登録者にメール通知するようにしてみました。

f:id:tyoshikawa1106:20150124134855p:plain


もし、Apexトリガー内でメール送信処理に失敗した場合は、送信メールエラーログオブジェクトにエラー情報を登録するようになっています。

f:id:tyoshikawa1106:20150124135932p:plain

応募者情報Chatter共有バッチ

新しく登録された応募者情報はスケジュールバッチで対象のChatterグループに共有するようになっています。

f:id:tyoshikawa1106:20150124141349p:plain

f:id:tyoshikawa1106:20150124141427p:plain


この時の投稿は対象の応募者レコードにも紐付いています。応募者について意見交換したい場合は、ここからChatterへ投稿したりコメントしたりして行います。

f:id:tyoshikawa1106:20150124141814p:plain


Chatter投稿を行うバッチ処理の対象になる応募者レコードは投稿済み項目にチェックが無いレコードとなります。バッチ処理が実行されたタイミングで投稿済み項目はチェック有りに更新されます。

f:id:tyoshikawa1106:20150124142411p:plain

レポート&ダッシュボードで応募者情報の分析

応募者情報に登録された性別、都道府県、開発経験年数、応募のきっかけと4つのダッシュボードでどのような応募者が多いのか簡単に分析できます。

f:id:tyoshikawa1106:20150124145700p:plain

Salesforce1モバイルアプリからのアクセス

Salesforce1アプリを利用することでモバイル端末からChatterグループに共有された応募者情報などに簡単にアクセスできます。

f:id:tyoshikawa1106:20150124145754p:plain:w300


Chatterの投稿には応募者レコードへのリンクが付いているので、タップ1つで詳細ページへ簡単に移動できます。

f:id:tyoshikawa1106:20150124145917p:plain:w300


BlogやGitHubなどのURLリンクを選択するとSalesforce1から対象ページに簡単にアクセスできます。Salesforce1のページに戻りたい場合は、左上の完了ボタンを選択することですぐに戻ることができます。

f:id:tyoshikawa1106:20150124145956p:plain:w300

Salesforce1 & Chatter

応募者の評価や意見交換はChatterで行います。パブリッシャーアクションのアンケート機能などを利用することで評価結果を集計しやすくなります。

f:id:tyoshikawa1106:20141214230859j:plain:w300

Salesforce1 & Dashboard

Salesforce1アプリの標準機能でダッシュボードにアクセスできるのでPCからログインしなくもモバイル端末から簡単に分析情報を確認できます。

f:id:tyoshikawa1106:20150124151006p:plain:w300


以上がこのアプリの機能になります。

デモ動画


アプリ開発でハマったところ

Force.comサイトとRemoteObjects

Forced.comサイトのゲストユーザですが、プロファイルの設定でAPIの利用を有効化することができません。

ゲストユーザプロファイル

f:id:tyoshikawa1106:20150124152026p:plain

その他のプロファイル

f:id:tyoshikawa1106:20150124152146p:plain


その為、JavaScriptからSalesforceのAPIでクエリを実行したりレコードの登録処理を行ったりといったことができませんでした。しかし、Winter'15のバージョンアップでRemoteObjectsの機能が追加されたことにより、API権限の無いユーザでもJavaScriptからクエリの実行やレコードの登録処理を行うことが可能になりました。


今回、Force.comサイトから登録する機能をつけたのもゲストユーザでRemoteObjectsを利用してみたかったのが理由の1つです。


このゲストユーザからRemoteObjectsで登録処理を実行したときに次の問題に遭遇しました。

f:id:tyoshikawa1106:20150124153148p:plain


『この操作を実行する権限がありません』というエラーです。システム管理者ユーザで動作することを確認してゲストユーザで実行した際にこのエラーが発生したので、ゲストユーザはRemoteObjectsの機能が利用できないのかと一瞬ドキッとしました。


この問題の発生原因はすぐに気づけたのですが、サイトのゲストユーザのプロファイル設定で応募者オブジェクト作成権限がついていなかったことが原因です。

f:id:tyoshikawa1106:20150124154212p:plain


通常、Apexクラスで登録を行う場合は、作成権限に関係なく処理を実行できます。RemoteObjectsの場合は、作成権限が無いユーザが登録処理を実行した場合、今回のようなエラーになるみたいです。

Sites TemplateとBootstrapSF1

今回、画面の開発ではBootstrapSF1を利用することにしていました。このBootstrapSF1をサイト開発で必要になるサイトテンプレートを利用するための『apex:composition』タグと組み合わせたときにボタンなどの一部スタイルが適用されない問題に遭遇しました。

f:id:tyoshikawa1106:20150124160021p:plain


『apex:composition』タグ以外にも『apex:inculde』タグなど別のVisualforceを読み込むような処理を行った際に発生するみたいです。この問題の解決方法は見つからなかったため、応募者登録のページではサイトテンプレートの利用は諦めることにしました。

サイトゲストユーザのApexトリガーによるChatter投稿

今回、Chatterへの投稿はスケジュールバッチを利用しましたが、開発当初はApexトリガーで対応する予定でした。応募者のINSERTトリガー用意してゲストユーザで実行してみた結果がこちらです。

f:id:tyoshikawa1106:20150124162720p:plain

caused by: System.DmlException: Insert failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, Entity is read-only: FeedItem: []

Exceptionエラーということで、システム管理者ユーザで実行できたものがゲストユーザではできませんでした。サイトのゲストユーザにはChatter関連の権限がなかったのでその関係じゃないかと思います。


この問題を回避するためにApexトリガーでのChatter共有は諦め、システム管理者権限で実行できるスケジュールバッチでのChatter共有に変更しました。

Chatter API in Apexの利用

Chatterへの共有処理は、FeedItemオブジェクトをINSERTする方法で考えていました。しかし、投稿時にリンクやメンションをつけたくなったため、FeedItemオブジェクトではなくChatter API in Apexを利用することにしました。


このChatter in Apexですが、あまり詳しくなかったのでいろいろと調べてみるとWinter'15のバージョンアップで一部クラスに変更があったみたいです。

f:id:tyoshikawa1106:20150124165616p:plain

Salesforce Winter '15 リリースノート


最近変更されたばかりの機能だったため、情報があまり見つからず使い方を理解するのに苦労しました。

f:id:tyoshikawa1106:20150124170642p:plain

Chatter in Apexとテストクラス

Chatter in Apexを使った機能の実装ができた後テストクラスをつくる際に次のエラーに遭遇しました。

f:id:tyoshikawa1106:20150124171631p:plain


テストクラスの作成ではあまり推奨されない『@isTest(SeeAllData=true)』の宣言をしてくださいというエラーです。テストクラス全体に『SeeAllData=true』を適用するのはあまり良くないので対象のメソッドにのみ適用させることで解決しました。

Chatter in Apex大量件数のテスト

Chatter in Apexの処理はConnect.Apiというように処理を書くので、投稿処理はDMLの制限なのかAPIの制限なのか少し気になっていたのですが、大量件数の処理の際に確認できました。

f:id:tyoshikawa1106:20150124203559p:plain


『Too many DML statements』ということでDMLの制限が適用されるみたいです。Chatter投稿を行う『postFeedElement』という処理をループ内で実行しているので対象レコードが200件のテストを行った際にこのエラーが発生しました。通常のINSERT処理と同じようにループ内でリストを用意してループの外で一括登録というように処理を実行すればこの問題は解決します。


・・・と考えたのですが、この『postFeedElement』という処理の部分をリスト化する方法が見つかりませんでした。どうしても解決方法が見つからなかったためここはバッチサイズの指定を通常の200から100に下げることで問題を回避しました。

アプリ開発でこだわったところ

カスタム設定の利用

スケジュールバッチでChatter共有する際にChatterグループと紐付けを行っているのですが、この紐付け対象となるChatterグループのグループ名はカスタム設定に持たせることにしました。これによりグループ名を変更したくなった際にApexコードの変更を行わずに対応できるようになります。

f:id:tyoshikawa1106:20150124223538p:plain

f:id:tyoshikawa1106:20150124223642p:plain


カスタム設定には『階層』と『リスト』の2種類の設定種別が用意されていますが、今回は『階層』の方を選択しました。『階層』は組織またはプロファイル毎に値を指定できますが、リスト型の場合は同じ項目を持つ複数のレコードという感じで値を指定します。複数のレコードを用意する必要は無いので『階層』の方となっています。

f:id:tyoshikawa1106:20150124224200p:plain


ちなみにカスタム設定の『階層』は数式項目で利用することができます。システム情報的な値として使用する場合は『階層』の方が便利だと思います。

カスタム表示ラベルの利用

画面に表示するラベルやメッセージなどはカスタム表示ラベルの値を表示するようにしています。

f:id:tyoshikawa1106:20150124224519p:plain


カスタム設定のときと同じようにコードの変更をせずにラベルやメッセージを簡単に変更できるようになります。今回、BootstrapSF1を利用した影響で『apex:include』タグが利用出来ませんでしたが、JavaScriptも静的リソースではなくVisualforceとして外出しして『apex:include』タグで呼び出すことで、JS内でカスタム表示ラベルが利用できるようになるので便利だと思います。

Apexクラスとテストクラス

テストクラスは1クラス、1テストクラスになるように用意しました。1つのテストクラスで複数のクラスをテストするより、テストもメンテナンスもしやすくなります。

f:id:tyoshikawa1106:20150125012538p:plain

テストデータ作成用の共通クラス

テストクラス内で用意するデータを作成する共通クラスを用意しています。これにより必須項目や入力規則が追加されたときの修正箇所を少なくできると思います。また、対象オブジェクトのレコード作成に必要な項目をここで確認できるようになるので便利だと思います。

f:id:tyoshikawa1106:20150125015206p:plain

Force.comサイトのエラーページ

Force.comサイトで存在しないURLを指定したりするときに表示されるエラーページを用意しました。

f:id:tyoshikawa1106:20150125015840p:plain


サイト設定のエラーページで指定できます。

f:id:tyoshikawa1106:20150125020006p:plain


ここの設定を行わないとデフォルトで用意されているエラーページが表示されてしまいますが、そのページは開発したサイトの雰囲気とは一致しないのであまり好ましくないと思います。サイトテンプレートなどを利用することで数行のコードで用意できるのでサイト開発の際にはできるだけ用意するようにした方がいいと思います。

f:id:tyoshikawa1106:20150125020425p:plain

GitHub Link

ハックチャレンジで作成したアプリのコードと説明はGitHubで公開してあります。

Salesforce DUG Meetup 2015 新年会

上の話をSalesforce DUG Meetup 2015 新年会でお話させて頂きました。
そのときのスライドはこちらになります。