Office 365 API : JavaScript Library for Cordova


2014/10 : 更新を反映

環境 :
Visual Studio 2013 Update 2
Office 365 API Tools
Multi-Device Hybrid Apps for Visual Studio 2013

Office 365 API

(Skype API は こちら)

こんにちは。

今週は、前回書ききれなかった Office 365 API の JavaScript Library について記載します。

最近発表された調査結果でも iPhone、Android といったマルチデバイスに対応したアプリは、アプリ全体の 40 % を上回るそうですが、ここで使用する Apache Cordova は HTML / CSS / JavaScript による開発プラットフォームを提供することで、こうしたクロス デバイスのアプリ開発を実現します。
そして、最新の Visual Studio では「Multi-Device Hybrid Apps」という Cordova 用の開発環境が提供されています。

本日紹介する Office 365 API の JavaScript Library は、この Cordova での利用を想定したライブラリーです。

 

Setup

まず、Visual Studio に Cordova の開発環境をセットアップします。

Visual Studio 2013 に、Visual Studio 2013 Update 2Multi-Device Hybrid Apps for Visual Studio 2013、さらに、前回紹介した Office 365 API Tools – Preview もインストールしておいてください。(Multi-Device Hybrid Apps for Visual Studio 2013 をインストールすると、のちほど紹介するエミュレーターの関係で、Chrome ブラウザーも同時にインストールされます。)

補足 : Multi-Device Hybrid Apps for Visual Studio 2013 をインストールするには Windows 8.1 以上が必要です。 (残念ながら、Windows Server はダメみたいです。) CTP 3 で Windows 7, 8 や Windows Server による開発が可能になりました。(2014/08/05 追記)

補足 : 実行時に Android SDK に関連するエラーが表示される際は、野呂さんが書いてくれている「Visual Studio 2013 で Cordova で HelloWorld を作ってみよう (Multi-Device Hybrid Apps)」を参照してください。

 

Programming

では、早速、Office 365 API の JS Library を使った Cordova アプリを作成してみましょう。

まず、Visual Studio 2013 を起動し、[JavaScript] – [Multi-Device Hybrid App] から [Blank App] を選択して Cordova のプロジェクトを新規作成します。

Office 365 API Tools は Cordova の開発プロジェクトに統合されています。
プロジェクトを右クリックして、[追加] – [接続済みサービス] (Connected Services) を選択します。表示されるウィザードで、左ペインから [Office 365 APIs] を選択して [Sign in] リンクをクリックし、Office 365 のテナントにログイン (Sign in) します。

SignIn すると、Microsoft Azure Active Directory (Azure AD) への Application 追加や Permission 設定などをおこなうための画面が表示されますので、前回同様、Permission 設定をおこないます。
今回は Exchange Online から Mail の Read をおこなう簡単なアプリケーションを作成するので、下図の通り [Mail] の [Read User’s Mail] の Permission を設定します。

上記の設定をおこなうと、前回同様、裏側で、Microsoft Azure Active Directory (Azure AD) への Application 追加と Permission 設定がおこなわれます。
またこの設定と同時に、プロジェクトに、o365adal.js, exchange.js など必要なライブラリーが挿入されます。(下図)

補足 : 開発者自身で必要なライブラリーのロードだけをおこないたい場合には、NuGet から Microsoft Office 365 APIs Client Libraries for JavaScript (Microsoft.Office365.ClientLib.JS) をインストールしてください。

Cordova プロジェクトでは、まず、プログラミングを開始する前に、config.xml を編集して、使用する機能に応じた Plugins (Capability) の設定をおこないます。
Office 365 API を使用する場合は、前回解説したように Web Browser 用のコンポーネント (Android の WebView、iOS の UIWebView など) が必要となるため、今回のプロジェクトでは下図の [InAppBrowser] の Plugin (Capability) が必要ですが、実は、上述の Office 365 API Tools の設定により、この設定が自動的におこなわれるので、追加の設定は不要です。

通常の開発だと、jquery mobile, AngularJS など好みの JavaScript ライブラリーやフレームワークと組み合わせて UI デザインなども行いますが、今回は Office 365 API を紹介することが目的なので、jquery も AngularJS も使用せずバニラな感じで作ってみましょう。(なお、de:code の私のデモでは、見栄えを考慮して bootstrap を使用しました。bootstrap を使えば、下記サンプルのような table の列幅の面倒な制御なども不要です。)

今回のサンプルでは、「Get Mails !」というボタンを配置して、このボタンを押したら Office 365 にログインをおこなって、Exchange Online から Inbox の Mail を取得して table に挿入します。下記のコードになります。

index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Office365APIJSSample</title>

  <link href="css/index.css" rel="stylesheet" />
  <script src="services/office365/scripts/InAppBrowserOverride.js"></script>
  <script src="services/office365/settings/settings.js"></script>
  <script src="services/office365/scripts/utility.js"></script>
  <script src="services/office365/scripts/o365auth.js"></script>
  <script src="services/office365/scripts/exchange.js"></script>
</head>
<body>
  <h4>
    Get all inbox e-mail
    <input id="getInboxBtn" type="button" value="Get Mails !" />
  </h4>

  <table id="msgTbl" style="table-layout: fixed;" border="1">
    <colgroup>
      <col class="w_from" />
      <col class="w_subject" />
    </colgroup>
    <thead>
      <tr>
        <th>From</th>
        <th>Subject</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>

  <!-- Cordova reference, this is added to your app when it's built. -->
  <script src="cordova.js"></script>
  <script src="scripts/index.js"></script>
</body>
</html>

index.js

var O365APIJSSample;
(function (O365APIJSSample) {
  "use strict";

  (function (Application) {
    function initialize() {
      document.addEventListener('deviceready', onDeviceReady, false);
    }
    Application.initialize = initialize;

    function onDeviceReady() {
      document.addEventListener('pause', onPause, false);
      document.addEventListener('resume', onResume, false);

      // button click event
      var getInboxBtn = document.getElementById("getInboxBtn");
      getInboxBtn.addEventListener("click", getInbox);
    }

    function onPause() {
    }

    function onResume() {
    }

    function getInbox() {
      var ctx = new O365Auth.Context();
      ctx.getIdToken('https://outlook.office365.com/').then((function (id_token) {
        var client = new Microsoft.OutlookServices.Client(
          'https://outlook.office365.com/api/v1.0',
          id_token.getAccessTokenFn('https://outlook.office365.com'));
        client.me.messages.getMessages().fetch().then(function (items) {
          items.currentPage.forEach(function (item, idx, arr) {
            var msgTbl = document.getElementById("msgTbl");
            var row = msgTbl.insertRow(-1);
            var cel1 = row.insertCell(0);
            cel1.appendChild(document.createTextNode(item.sender.emailAddress.address));
            cel1.setAttribute("nowrap", "nowrap");
            var cel2 = row.insertCell(1);
            cel2.appendChild(document.createTextNode(item.subject));
          });
        }, function (reason) {
          // o365 api error
          // see reason._message ...
        });
      }).bind(this), function (reason) {
        // authentication failed
        // see reason._message ...
      });
    }

  })(O365APIJSSample.Application || (O365APIJSSample.Application = {}));
  var Application = O365APIJSSample.Application;

  window.onload = function () {
    Application.initialize();
  };
})(O365APIJSSample || (O365APIJSSample = {}));
//# sourceMappingURL=index.js.map

 

Debug & Execute

F5 ボタンを押してデバッグ実行をおこなってみましょう。

Cordova では Local Machine, Simulator などを使用したデバッグが可能ですが、プロジェクトの既定の設定では、Ripple という Chrome を使った Android Emulator が起動します。(この設定は、プロジェクトのプロパティで構成できます。必要な Emulator をインストールしておいてください。)
下図のような [Get Mails !] ボタンの付いた画面が Chrome 上に表示されるので、このボタンを押します。すると、いつものように、Office 365 の SignIn 画面が表示され、SignIn を完了すると Exchange Online から Inbox メールが検索されて、下図の通り結果が表示されるでしょう。

補足 : iOS 用アプリとしてデバッグする場合には、OSX 用の vs-mda-remote が使用できます。

なお、Ripple のエミュレーターの場合、SignIn の際に Chrome の別画面がポップアップするかと思いますが、実機では、上述で plugin されていた Cordova の InAppBrowser を使用して、デバイス上にログイン画面が表示されます。

また、ログインに成功すると、利用者に Exchange の Mail の Read 権限を付与するための Consent UI (下図) が表示されるでしょう。(この Consent UI の詳細については「Azure Active Directory の Common Consent Framework」を参照してください。)

なお、2 回目以降、このアプリケーションにアクセスしても Login (SignIn) 画面は表示されません。理由は、JavaScript Library で、取得した access_token, refresh_token などの情報を localStorage に保持しているためです。
このため、再度、ログイン画面を表示して検証 (デバッグ) をおこなう場合は、以下のプログラムを実行し、この Cordova の cache (localStorage) を削除しておきましょう。

window.localStorage.clear();

 

typescript との better together

上記のプロジェクトで scripts/typing フォルダーを見てください。下図のように、o365adal.d.ts、aadgraph.d.ts、exchange.d.ts などが入っているのが確認できますね。
そうです、Office 365 API の JavaScript Library は、typescript の定義ファイルも提供しています。(インテリセンスがばっちり使えます。)

例えば、上記の index.js を削除して、以下の index.ts ファイルに変えてみてください。
実行すると、ビルドの際に tsc.exe (typescript のコンパイラー) が動作して index.js ファイルを生成し、上記と同様の結果となるでしょう。

index.ts

module O365APIJSSample {
  "use strict";

  export module Application {
    export function initialize() {
      document.addEventListener('deviceready', onDeviceReady, false);
    }

    function onDeviceReady() {
      document.addEventListener('pause', onPause, false);
      document.addEventListener('resume', onResume, false);

      var getInboxBtn = document.getElementById("getInboxBtn");
      getInboxBtn.addEventListener("click", getInbox);
    }

    function onPause() {
    }

    function onResume() {
    }

    function getInbox() {
      var ctx = new O365Auth.Context();
      ctx.getIdToken('https://outlook.office365.com/')
        .then((function (id_token: O365Auth.Token) {
          var client = new Microsoft.OutlookServices.Client(
            'https://outlook.office365.com/api/v1.0',
            id_token.getAccessTokenFn('https://outlook.office365.com'));
          client.me.messages.getMessages().fetch()
            .then(function (items) {
              items.currentPage.forEach(function (item, idx, arr) {
                var msgTbl =  document.getElementById("msgTbl");
                var row =  msgTbl.insertRow(-1);
                var cel1 =  row.insertCell(0);
                cel1.appendChild(document.createTextNode(item.sender.emailAddress.address));
                cel1.setAttribute("nowrap", "nowrap");
                var cel2 = row.insertCell(1);
                cel2.appendChild(document.createTextNode(item.subject));
              });
            }, function (reason) {
              // o365 api error
              // see reason._message ...
            });
        }).bind(this), function (reason) {
          // authentication failed
          // see reason._message ...
        });
    };
  }

  window.onload = function () {
    Application.initialize();
  }
}

これは Office 365 API のメリットではなく typescript そのものの一般的メリットですが、private メソッドや public メソッドなど型の情報が明確なので、いろいろと恩恵を受けることができます。
例えば、上記の for loop の箇所は、実は、JavaScript を使った場合、以下のような誤った書き方も可能です。(_data は undocumented なメンバーですので、使わないようにしてください。) しかし、typescript では、_data などは private メンバーなので error になります。

. . .
for (var i = 0; i < items._data.length; i++) {
  var msgTbl = document.getElementById("msgTbl");
  var row = msgTbl.insertRow(-1);
  var cel1 = row.insertCell(0);
  cel1.appendChild(document.createTextNode(items._data[i]._Sender._EmailAddress._Address));
  cel1.setAttribute("nowrap", "nowrap");
  var cel2 = row.insertCell(1);
  cel2.appendChild(document.createTextNode(items._data[i]._Subject));
}
. . .

また、型が明示的に定義されているため、下図のように、JavaScript のときには Intellisense が表示されなかった箇所 (戻り値の型が不定な箇所) でも、追加の設定をする必要なく Intellisense が表示されます。まるで C# でコードで書いているかのようにプロパティを見ながらどんどんコードを書いていくことができますね。

このように、Office 365 API においても、JavaScript を使用せず、むしろ typescript でコードを記述するほうが、開発生産性やコード品質などが飛躍的に向上すると思いますので、是非活用してみてください。

Comments (0)

Skip to main content