はまったりひらめいたり…とか…

.Net系プログラムで勉強したこととか嵌ったことについて書いたりします。

フロントエンドのMicrosoft Graphことはじめ

はじめに

使用するライブラリは下記バージョンとなります。

また、Graph使用部分はAngularになるべく依存しない形で構成していますが

手を抜きたい箇所がちょこちょことあるのでベースは下記Ver.のAngularで作成しています。

  • @angular/cli: 8.3.0
  • Angular: 8.2.3

それぞれの内容について記事をご覧になるタイミングによっては、画面、仕様が変更されている可能性があるのでご留意くださいませ。

なにがしたいか

フロントエンドだけでMicrosoft Graphから情報取得したい。

以上。

実装

1.Azure AD Applicationの準備

例のごとく、Graphへのアクセストークン取得用のAzure AD Applicationを作成します。

今回はOutlookで管理しているメールの情報などを取得するため

Microsoft個人アカウントにアクセスできるアプリケーションを作成します。

ADアプリを作成する際に「個人アカウント」が使用できるタイプを選択します。

f:id:TakasDev:20190910223352p:plain

2.ライブラリのインストール

Httpの通信ベースでもGraphからは情報は取得できますが

.net同様公式からClientライブラリが提供されていますので、それを使用します。

インストールするライブラリは下記のとおりです。

msal.js

今回、個人アカウントの情報にアクセスするためAzureAdv2アプリケーションを使用します。

そのためADALではなくMSALを使用します。

Graphにアクセスする際に使用するアクセストークンの取得に使用します。

npm install msal --save

@microsoft/microsoft-graph-client

Microsoft Graphとやり取りすためのライブラリです。

npm install @microsoft/microsoft-graph-client --save

@microsoft/microsoft-graph-types

Clientには型情報がついてこないようです。

TypeScriptを使用している方は@microsoft/microsoft-graph-types もインストールしておくと

Graphから取得したデータのマッピングが幾分か楽になると思います。

後述するクエリオプションの$selectを使用するのであればいらないかなとは思います。

npm install @microsoft/microsoft-graph-types --save-dev

3. msal.jsのイニシャライズ

Graphからデータを取得するためには、何はともあれアクセストークンを取得する必要があります。

リダイレクト時に読み込まれるページ、あるいはリダイレクト時に読み込まれるJS等でmsalのイニシャライズを行います。

JSでClassを使用している場合はconstructorでイニシャライズを行います。

とにかくリダイレクト時にUserAgentApplicationが生成されるようにしておきます。

import * as msal from 'msal';

export class AuthService {
    private authClient: msal.UserAgentApplication;
    constructor() {
        this.authClient = new msal.UserAgentApplication({
            auth: {
                clientId: '<ADアプリのClientID>',
                authority: 'https://login.microsoftonline.com/common'
            }
       });
    }
}

UserAgentApplicationのコンストラクタの引数で、使用するAzureADアプリケーションの情報を与えます。

今回Microsoft個人アカウントの情報を取り扱いたいので、authorityhttps://login.microsoftonline.com/commonを指定します。

テナントに紐づく情報のみに抑えたい場合はhttps://login.microsoftonline.com/<tenant-id>でOKです。

4. GraphClientのイニシャライズ

先に記述したとおり、Graphからデータを取得するためには

AzureADアプリケーションから提供されるアクセストークンが必要となります。

GraphClientはイニシャライズ時に、Graphアクセス用のトークンを取得するauthProviderを設定することができます。

Providerを指定せずにGraphアクセス時にヘッダにアクセストークンを設定する事も可能ですが

アクセス都度ヘッダを設定する処理を書くのも邪魔くさいと思うのでProviderを設定するほうがいいかなと思います。

import * as graph from '@microsoft/microsoft-graph-client';

export class GraphService {
    private client: graph.Client;
    
    constructor() {
        this.client = graph.Client.init({
            authProvider: async (done) => {
                const token = await this.authService.acquireToken({ scopes: [ 'mail.read' ] });
                if (token) {
                    done(null, token.accessToken);
                } else {
                    done('Can not Get Token', null);
                }
            }
        });
    }
}

authProviderとして、通信を行う際のトークン取得処理を記述します。

3.で設定したmsal.jsのファンクションacquireTokenを使用しアクセストークンを取得します。

acquireTokenの引数で指定するスコープ情報は、使用したいリソースの権限情報をドキュメントで参照してください。

例えば、Messageの権限情報はこちらから参照できます。

acquireTokenはPromiseで値を返却してくれるので、async-awaitでアクセストークンを取得し

authProviderのCallbackdoneの引数にトークンを指定し実行します。

done(error: any, accessToken: string | null)を引数で実行されるCallbackです。

トークンが取得できなかったりエラーが発生した場合はaccessTokenをnullにし、errorにエラー情報を格納し

逆にトークンが取得できた場合はaccessTokenに取得したトークンを設定し、errorにnullを格納し実行します。

5. Graphの実行

下準備は整いました。GraphClientを使用し、自分に送信されたメールの情報を取得してみようと思います。

export class GraphService {
    private client: graph.Client;
    ....
    getMessage() {
        this.client.api('/me/messages').get()
    }
}

処理を組み立てるときはMicrosoftのドキュメントにお世話になります。

例えばメッセージを一覧表示する - Microsoft Graph v1.0 | Microsoft Docs

.api()内のURLの指定は、この「HTTP要求」にあるアドレスを指定すればOKです。

と、いいますか、ページ下部にあるほぼそのまんまですね。

6. 複数ページにまたがるデータの取得

メールや予定表など、データ数がべらぼうに多そうなデータは一回のリクエストで全件データ取得できません。

あるいは自分で1回あたりの取得データの件数を絞ったりもできます。

アプリで Microsoft Graph データをページングする - Microsoft Graph | Microsoft Docs

その場合、@odata.nextLinkというプロパティに次ページのリンクURLが格納された状態で、Graphからデータが帰ってきます。

なので、全件データを取得したい場合は@odata.nextLinkに、次ページへのリンクが格納されなくなるまでループを回し続けなければならないというわけですね。

private messageList: Message[] = []; /* Message:@microsoft/microsoft-graph-types で提供されている型情報 */

// 初回は「/me/messages」で以降はnextlinkのURLが引数に
getMessage(url: string) {
    this.client.api(url).get().then(x => {
        (x.value as Message[]).forEach(element => {
            this.messageList.push(element);
        });
        if (x['@odata.nextLink'] !== '') {
            const nestNextLink = x['@odata.nextLink'];
            this.getMessage(nestNextLink);
        }
    });
}

7. クエリオプション

特定条件でのデータの絞り込み。取得データを特定の項目のみにする絞り込み等が行なえます。

何が行えるかは下記のドキュメントを参照してください。

クエリ パラメーターを使用して応答をカスタマイズする - Microsoft Graph | Microsoft Docs

その中からいくつかのクエリオプションを見てみます。

$select

取得する項目を指定する事ができます。

通信でどのような項目のやり取りされるかはAPIドキュメントを参照すればOKです。

今回使用してみた/messageのプロパティは下記のドキュメントで確認できます。

メッセージ リソースの種類 - Microsoft Graph v1.0 | Microsoft Docs

このプロパティから取得したい項目を選択することになります。

GraphClientでは.selectがそれにあたります。

下記のように.selectで取得したい項目名を指定します。

export class GraphService {
    private client: graph.Client;
    ....
    getMessage() {
        this.client.api('/me/messages')
            .select('subject, receivedDateTime')
            .get()
    }
}

messageのメール表題のsubjectと受信日時のreceivedDateTimeを取得しています。

$filter

取得する項目の絞り込みを行うことができます。

$select同様、絞り込む項目はAPIドキュメントを参照します。

GraphClientでは.filterを下記のように使用します。

filterの論理演算子については下記のドキュメントを参照してください。

クエリ パラメーターを使用して応答をカスタマイズする - Microsoft Graph | Microsoft Docs

export class GraphService {
    private client: graph.Client;
    ....
    getMessage() {
        this.client.api('/me/messages')
            .filter('isRead+eq+false')
            .get()
    }
}

messageの内未読のもののみを取得しています。

まとめ

フロントエンドのみでGraphのデータにアクセスする方法を確認しました。

  • Graphへのアクセスは@microsoft/microsoft-graph-clientを便利に使用しましょう
  • Graphの使用の仕方は公式ドキュメントが一番参考になります
  • クエリオプションを使用してGraphを使いこなしましょう

今回検証に使用したサンプルは下記のリポジトリとなります。

github.com