からめもぶろぐ。

SharePoint が得意なフレンズなんだね!すごーい!

Microsoft Graph でログイン ユーザーが組織アカウントか Microsoft アカウントかどうかを識別する

なんかいい方法あったら教えてください。

/me で判断する

とりあえず思いつく /me ではあまり違いがわかりません。

  • 組織アカウントの場合
{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
    "id": "00000000-0000-0000-0000-000000000000",
    "businessPhones": [],
    "displayName": "Example User",
    "givenName": "",
    "jobTitle": null,
    "mail": "example@example.onmicrosoft.com",
    "mobilePhone": null,
    "officeLocation": null,
    "preferredLanguage": "ja-JP",
    "surname": "",
    "userPrincipalName": "user@example.onmicrosoft.com"
}
  • Microsoft アカウントの場合
{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
    "displayName": "Example User",
    "surname": "",
    "givenName": "",
    "id": "0000000000000000",
    "userPrincipalName": "example@outlook.com",
    "businessPhones": [],
    "jobTitle": null,
    "mail": null,
    "mobilePhone": null,
    "officeLocation": null,
    "preferredLanguage": null
}

userPrincipalName のドメイン名で判別するか、id が CID (ULong?) か GUID かで判断するか、どちらにしてもあまり美しくない感じがします。

/organization で判断する

/organization は Microsoft アカウントの場合は空の配列を返します。こっちのほうがよさそう。

  • 組織アカウントの場合
{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#organization",
    "value": [
        {
            "id": "00000000-0000-0000-0000-000000000000",
            "deletedDateTime": null,
            "businessPhones": [],
            "city": null,
            "country": null,
            "countryLetterCode": null,
            "displayName": "example",
            "marketingNotificationEmails": [],
            "onPremisesLastSyncDateTime": null,
            "onPremisesSyncEnabled": null,
            "postalCode": null,
            "preferredLanguage": null,
            "privacyProfile": null,
            "securityComplianceNotificationMails": [],
            "securityComplianceNotificationPhones": [],
            "state": null,
            "street": null,
            "technicalNotificationMails": [],
            "assignedPlans": [],
            "provisionedPlans": [],
            "verifiedDomains": [
                {
                    "capabilities": "Email, OfficeCommunicationsOnline",
                    "isDefault": false,
                    "isInitial": true,
                    "name": "example.onmicrosoft.com",
                    "type": "Managed"
                }
            ]
        }
    ]
}
  • Microsoft アカウントの場合
{
    "@odata.context": "https://graph.microsoft.com/beta/$metadata#organization",
    "value": []
}

そもそも判断する必要なんてないんだよ

アカウントでサポートされていない API の場合はエラーが返ります。例えば Microsoft アカウントで /me/manager を実行した場合は 404 エラーになります。

{
    "error": {
        "code": "",
        "message": "No HTTP resource was found that matches the request URI 'https://outlook.office365.com:444/profile/v1.0/users('CID:0000000000000000')/profile/manager?api-version=AGSV1-internal'.",
        "innerError": {
            "request-id": "00000000-0000-0000-0000-000000000000",
            "date": "2018-07-10T00:00:00"
        }
    }
}

呼んでみてエラーだったら諦める、というのも考え方としてはアリかもしれません。

Microsoft Graph の Bookings REST API (Preview) について

Microsoft Bookings という Office 365 Bussiness Premium 向けのサービスがあります。*1

Microsoft Bookings、オンライン スケジューリングおよび予約アプリ

オンラインの予約受付サービスで、顧客はサービスやスタッフ、金額を選択して予約を入れることができ、スタッフはそれを Outlook やモバイル アプリで管理できます。上記のサイトでは、ヘア サロンや金融サービス、不動産業者のデモを見ることができ、予約のプロセスを簡単に確認することができます。

実際に予約ページを開いてみたところ。

カレンダーから担当と時間を簡単に選べます。

とはいえ、デモを見てもわかる通り、予約ページは非常にシンプルなので、カスタマイズしたり、あるいは既存の Web サイトと統合したい、という場合があるかもしれません。あるいは、bot サービスから予約を受け付けたり、予約情報をスタッフで共有するために他のサービスと連携したい、という要望もあるかと思います。そのような場合のために、Microsoft Graph の Bookings REST API を使うことができます。

Customer booking - Documentation - Microsoft Graph

現在プレビューではありますが、Graph Explorer からも試すことができます。v1.0 エンドポイントだと怒られてしまうので、beta エンドポイントに変更するのを忘れずに。

予約 (appointments)、サービス (services)、顧客 (customers)、スタッフ (staffMembers) に対する CRUD 操作ができる他、予約の公開 (publish) または非公開 (unpublish) を行うこともできます。まあだいたいの管理機能は Web の管理画面やモバイル アプリからできるので、あまり使うかというと微妙なところではありますが。

*1:サービスの内容からして中小企業向けですが、エンタープライズ企業 (E3 や E5 を契約している企業) でも使用できます。詳しくは Office 365 Enterprise Edition での Microsoft Booking の有効化 - SharePoint Technical Notes が参考になります。

Microsoft Graph で会議室の予定表を取得する

元ネタ

Graph API 会議室取得の失敗について

結構よくある話で OAuth のハマりどころの最たるものが「アプリケーションにアクセス許可を与えたのにデータが取れない」となることなのです。
上記の「Calendars.ReadWrite.Shared を付けたのに会議室の情報が取れない」というのは、アプリを使用するユーザーに権限がないからで、これはむしろ正しい動作な訳です。「委任されたアクセス許可」という名前が表す通りですね。
公式ドキュメントにも説明があります。

委任されたアクセス許可の場合、アプリの有効なアクセス許可は、アプリに付与されている委任されたアクセス許可 (同意によって付与) と現在サインインしているユーザーの特権が重なる範囲に収まる最小権限になります。

アクセス許可 - ドキュメント - Microsoft Graph

じゃあどうするのというと、代わりに「アプリケーションのアクセス許可」を使うことになります。
ただし、こちらは、バックグラウンドで動作するアプリケーションを想定しているので、気を付けなければなりません。例えば、アクセス許可に「Calendars.Read」を付けると、特定のアカウントだけではなく、すべてのアカウントの予定表が見られるようになります。これはセキュリティ上のリスクが高くなるので、フロントエンドで動作するアプリでの使用は特に気を付けなければなりません。

前置きが長くなったのでサンプル コードです。今回は MSAL を使っています。

using Microsoft.Identity.Client;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace ConsoleApplication1
{

    public static class Program
    {

        private static readonly string TenantId = "<Tenant ID>";
        private static readonly string Authority = $"https://login.microsoftonline.com/{TenantId}/v2.0";
        private static readonly string RedirectUrl = "<Redirect URL>";
        private static readonly string ClientId = "<App ID>";
        private static readonly string ClientSecret = "<App Secret>";
        private static readonly string ResourceId = "https://graph.microsoft.com/.default";
        private static readonly string RequestUrl = "https://graph.microsoft.com/v1.0/users/{0}/calendarView?StartDateTime={1:s}&EndDateTime={2:s}";

        private static void Main(string[] args)
        {
            var userId = "<Resource ID>";
            var startDateTime = DateTime.Today;
            var endDateTime = DateTime.Today.AddDays(1);
            GetCalendarAsync(userId, startDateTime, endDateTime).GetAwaiter().GetResult();
        }

        private static async Task GetCalendarAsync(string userId, DateTime startDateTime, DateTime endDateTime)
        {
            var oauthClient = new ConfidentialClientApplication(ClientId, Authority, RedirectUrl, new ClientCredential(ClientSecret), null, new TokenCache());
            var oauthResult = await oauthClient.AcquireTokenForClientAsync(new[] { ResourceId });
            var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", oauthResult.AccessToken);
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = await httpClient.GetAsync(string.Format(RequestUrl, userId, startDateTime, endDateTime));
            var responseContent = await responseMessage.Content.ReadAsStringAsync();
        }

    }

}

ちなみに、findMeetingTimes は「アプリケーションのアクセス許可」では動作しないので、こっちを使いたい場合はメールボックスにアクセス許可を付けるしかないですね。

PowerApps PowerShell についてフィードバックを出してみた

ということで PowerApps 用の PowerShell があることを教えていただいたので、ちょっと試してみて困ったところなどをフィードバックしてみました。

powerusers.microsoft.com

Add-PowerAppsAccount でログインするときに IdToken のパターンによってエラーになるケースがあったので。

powerusers.microsoft.com

現状スクリプトを直接ダウンロードなんですが、早く PowerShell Gallery に公開してほしいですね。そのためには現状モジュールの設計もいまいちっぽいので見直しが必要そうですが。

powerusers.microsoft.com

EnvironmentName ってパラメーターなのに GUID なのおかしくない?という話。

スクリプトなのでソース見られるんだけど GitHub で公開してないんでしょうか。全体的にまだまだなのでオープンソースにしたほうがいい気がします。

SharePoint Framework 1.5 がリリースされました

2018/06/05 に SharePoint Framework 1.5 がリリースされました。

Office Dev Center - Announcing SharePoint Framework version 1.5: new tools and a beta preview

機能追加としては、パッケージ マネージャーとして npm のほかに pnpm と Yarn が使えるようになりました。これによりプロジェクトを作成するときの時間と容量を節約することができるようになりました。軽くベンチマークをとったところ以下のような感じでした。

npm pnpm Yarn
処理時間 (1 回目) 238,031 ミリ秒 192,647 ミリ秒 240,827 ミリ秒
処理時間 (2 回目) 207,439 ミリ秒 95,366 ミリ秒 129,975 ミリ秒
処理時間 (3 回目) 212,534 ミリ秒 103,512 ミリ秒 141,791 ミリ秒
使用容量 338 MB 314 MB 365 MB

Yarn は msi または chocolatey によるインストールが必要ですが、pnpm は npm からインストールできます。お手軽さを考えると pnpm ですかね。

また、プレビュー機能を評価するための plusbeta オプションが追加されました。これにより、通常はプレビュー機能は含まれず、yo で plusbeta オプションを指定した場合のみ利用できるようになります。ただし、これまでプレビューとして公開されている機能 (MSGraphClient など) を使う場合は plusbeta オプションは必要ありません。

そして、プレビュー機能として Dynamic Data が追加されました。これはクラシックの Web パーツ接続に相当するもので、Web パーツ同士でのインタラクティブなデータのやり取りを行うことができます。サンプルとしてリスト アイテムを選択するとリスト アイテムの詳細情報と地図を連動して表示する Web パーツが取り上げられています。

github.com