OneDrive API を使った通知 (Webhook) と同期 (Synchronization)


Office 365 API

(Skype API は こちら)

現在 (2016/04)、Microsoft Graph では、File の通知 (Webhook) と同期はサポートしていません。

こんにちは。

OneDrive API で Webhook を使った通知 (Notification) がサポートされましたので、今回は、OneDrive API によるファイル更新の通知 (Webhook) と、変更箇所の同期 (Synchronization) について紹介します。(同期は以前からリリースされている機能です。)
通知や同期と組み合わせることで、OneDrive 上のファイルと双方向で連携する高度なアプリケーション開発が可能です。

なお、現在 (2016/04)、通知 (Webhook) は、OneDrive (旧 SkyDrive) で使用可能で、OneDrive for Business については展開中ですので (Limited Preview)、後者の方は、もう少々お待ちください。
今回のサンプルも、OneDrive (旧 SkyDrive) を使用しています。

 

Webhook による通知 (Notification)

基本的な通知 (Notification) のフローは「Outlook REST API : 通知 (Webhook) と同期 (Synchronization) を処理する」で紹介した概念同様、以下の手順で処理します。

  1. 特定アイテムに対する通知を要求します (Subscribe)
  2. Office 365 (OneDrive) は、登録された Webhook のアドレス (Callback 先の URL) を検証し、問題なければこの要求を受け入れます
  3. 対象のアイテムに変更が発生すると、Webhook により、登録されたアドレス (Callback 先の URL) に対して POST メソッドを発行 (通知) します

まず Developer は、「Office 365 API 入門」で解説した手順 (OneDrive for Business の場合) か、「Live Services (Outlook.com, OneDrive, etc) 開発」で解説した手順 (OneDrive の場合) で、あらかじめ Application を登録し、OAuth のフローを処理します。(利用者は、Application を登録する必要はありません。アプリケーションを提供する企業側で登録します。)

今回のサンプルでは OneDrive (旧 SkyDrive) を使用するため、Application Registration Portal に Application を登録してください。(なお、登録の際、必ず [Live SDK support] にチェックを付けてください。)

OAuth のフロー (token の取得) の際に使用する (OAuth の) scope は、通知を設定するアイテムに対する読み込み権限があれば良いので、onedrive.readonly (下記) で充分です。(逆に、権限のないアイテムに対して通知はできません。)

https://login.live.com/oauth20_authorize.srf
  ?response_type=code
  &client_id=b66af5e8-7218-43fb-a8a2-2e8aaa6dfdcf
  &scope=wl.signin%20onedrive.readonly
  &redirect_uri=https%3a%2f%2fmysample01.azurewebsites.net%2ftest.php

認証に成功して access token を取得したら、「OneDrive API を使ったアプリケーション開発」の手順で、対象のアイテム (ファイル、フォルダ) の item id を取得します。
今回、9C0AF81B735E29EA!16651 であったと仮定しましょう。

そして、このアイテムに対する通知を要求するため、下記の HTTP POST を発行 (Subscribe) します。(下記の EwBwAq1... には、取得した access token を設定します。)

POST https://api.onedrive.com/v1.0/drive/items/9C0AF81B735E29EA!16651/subscriptions
Accept: application/json
Authorization: Bearer EwBwAq1...
Content-Type: application/json; charset=utf-8

{
  "notificationUrl": "https://mysample01.azurewebsites.net/callback.php",
  "expirationDateTime": "2016-04-30T23:59:59.999Z",
  "scenarios": ["Webhook"]
}

成功すると下記の HTTP Response が返ります。
ここで一番重要なのが、Subscription id (下記太字) です。(この id は、このあとの処理で使います。)

HTTP/1.1 201 Created
Content-Type: application/json; odata.metadata=none
Location: https://api.onedrive.com/v1.0/drives('me')/items('9C0AF81B735E29EA!16651')/subscriptions('WLS_SubscriptionId_E31044A0-D34A-426B-B2FD-B24A5B8E9682')

{
  "createdBy": {
    "application": {
      "id": "1209601434"
    },
    "user": {
      "id": "9c0af81b735e29ea"
    }
  },
  "expirationDateTime": "2016-05-01T00:00:00.353Z",
  "id": "WLS_SubscriptionId_E31044A0-D34A-426B-B2FD-B24A5B8E9682",
  "muted": false,
  "notificationUrl": "https://mysample01.azurewebsites.net/callback.php",
  "resource": "/drives/0/items/9C0AF81B735E29EA!16651"
}

上記の通知要求 (Subscribe) の際、Office 365 (OneDrive) のサービスは、Callback 先 (上記例の https://mysample01.azurewebsites.net/callback.php) が受け入れを許可しているか否か確認するために、https://{callback url}?validationtoken=xxxxx の形式で POST 要求を出します。そこで、Callback 側では、下記の通り、受け取った validationtoken をそのまま Response Body に設定して、Status 200 を返すようにします。この設定により、Callback 側がスタンバイ OK であることを Office 365 側に伝えます。
この確認処理 (Webhook Validation Requests) に Pass しなければ、上記の Subscription 要求 (通知の要求) は失敗し、エラー ステータスが返ります。

<?php
  $validToken = $_GET['validationtoken'];
  if($validToken) {
    // Respond to Webhook Validation Requests
    header('Content-Type: text/plain');
    print($validToken);
    http_response_code(200);  
  }
  else {
    // Monitor Notification
    . . . 今後記述
  }
?>

以降は、そのアイテムに変更が発生するたびに、上記の Callback 先に以下が POST されます (Webhook)。

{
  "value": [
    {
      "subscriptionId": "WLS_SubscriptionId_E31044A0-D34A-426B-B2FD-B24A5B8E9682",
      "subscriptionExpirationDateTime": "2016-05-01T00:00:00.353Z",
      "userId": "9c0af81b735e29ea",
      "resource": "/drives/9c0af81b735e29ea/items/9C0AF81B735E29EA!16651"
    }
  ]
}

アプリケーション側ではこれを受け取って、必要な処理をします。
例えば、下記は、単純に受け取った要求を出力するサンプルです。(ngrok を使った HTTP Capture や、Azure の App Service Editor を使った Log の Console 出力などで、モニターしてみてください。)

<?php
  $validToken = $_GET['validationtoken'];
  if($validToken) {
    // Webhook Validation Requests
    header('Content-Type: text/plain');
    print($validToken);
    http_response_code(200);  
  }
  else {
    // Monitor Notification
    $bodytxt = file_get_contents('php://input');
    error_log($bodytxt);
    http_response_code(200);
  }
?>

情報を受け取った後、もし OneDrive に接続して処理するには、「Azure AD : Backend Server-Side アプリの開発 (Deamon, Service など)」で解説した Backend 連携を組み合わせるか、あるいは、ユーザーのデバイスに通知などをおこない、ユーザー権限でログインして処理を進めることになります。

また、Webhook で返ってくる情報は、上述の通り、基本的な情報しか含んでいないため、もし変更点のトラックなどが必要な場合は、後述する同期 (Synchronization) も組み合わせて実装します。

補足 : 現実の開発では、Webhook の Callback 内で重い処理は避けてください。(すぐに Webhook に応答してください。) もし、通知を受けて OneDrive への接続などが必要な場合は、例えば、処理をキューしておき、別スレッドでこのキューを処理するなど、設計上の工夫をします。

通知 (Subscription) を終了するには、下記の通り DELETE 要求を投げます。
(同様に、GET を使って、現在の通知の一覧なども取得できます。これらの解説は省きます。)

DELETE https://api.onedrive.com/v1.0/drive/items/9C0AF81B735E29EA!16651/subscriptions/WLS_SubscriptionId_E31044A0-D34A-426B-B2FD-B24A5B8E9682
Accept: application/json
Authorization: Bearer EwBwAq1...
HTTP/1.1 204 No Content

ファイルでなく、フォルダに対して通知 (Webhook) を設定した場合も、サブフォルダ (下の階層すべて) のファイルやフォルダが変更されるたびに (ファイル追加なども含め)、通知されます。

補足 :バッチ処理などの場合、通知が一気に多数到着するケースもあるので、現実の開発では、こうしたケースにも対応して設計してください。

 

ファイル/フォルダの同期 (Synchronization)

ここで紹介する同期 (Synchronizatoin) の API を使って、OneDrive 標準の同期クライアントのような処理を、皆さんのアプリケーションに実装できます。(OneDrive とカスタム アプリ間での同期機能、など)

概念は簡単です。(OData の仕様なので、基本的に「Outlook REST API : 通知 (Webhook) と同期 (Synchronization) を処理する」と同じです。)
まず、初回に全件取得しますが、この際、引き続く変更点のみを取得のための URL (後述する @odata.deltaLink) が返されるので、以降はこの URL を使って変更点のみを取得するというものです。

変更のトラッキングは、同期ポイント (フォルダ) から下の階層のすべてのフォルダ/ファイルが対象です。

では、実際の処理を見てみましょう。

まず、上述の通り、今回も、トラッキングの対象となるフォルダ (そこから下がすべて同期対象です) の item id を取得することからはじまります。
今回は、このフォルダの id を 9C0AF81B735E29EA!16650 と仮定します。

つぎに初回の全件取得ですが、下記の通り view.delta を使って HTTP GET 要求を発行します。
今回は、下記の通り、mysession.pptx、memo.txt の 2 つのファイルがあります。(下記の HTTP Response では、フォルダ自身と、この 2 つのファイルの、合計 3 つのオブジェクトが返されています。)

なお、もし大量にデータが存在する場合は、@odata.nextLink の Url が返されるので、これを使って次のデータを取り出せます。

GET https://api.onedrive.com/v1.0/drive/items/9C0AF81B735E29EA!16650/view.delta
Accept: application/json
Authorization: Bearer EwBwAq1...
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=none

{
  "@odata.deltaLink": "https://api.onedrive.com/v1.0/drives('me')/items('9C0AF81B735E29EA!16650')/view.delta?token=aTE09NjM1O...",
  "@delta.token": "aTE09NjM1O...",
  "value": [
    {
      "createdBy": {
        "application": {
          "displayName": "OneDrive website",
          "id": "44048800"
        },
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16650",
      "name": "onedrivetest001",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16650",
      "folder": {
        "childCount": 0
      },
      ...

    },
    {
      "@content.downloadUrl": "https://ubobfq.bn1304.livefilestore.com/y3mD_8uuMcYKN6HUsdgJ...",
      "createdBy": {
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16651",
      "name": "mysession.pptx",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16651",
      "file": {
        "hashes": {
          "sha1Hash": "4012C20CACDBBF7B8FB1BC67794702CCA4F19109"
        },
        "mimeType": "application/vnd.ms-powerpoint.12"
      },
      ...

    },
    {
      "@content.downloadUrl": "https://public.bn1304.livefilestore.com/y3mDK_awVY6_3tPGCfq8...",
      "createdBy": {
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16653",
      "name": "memo.txt",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16653",
      "file": {
        "hashes": {
          "crc32Hash": "7B1B1141",
          "sha1Hash": "4D87F25AE098F6C69C988F992773F0D80A33821B"
        },
        "mimeType": "text/plain"
      },
      ...

    }
  ]
}

以降、上記の @odata.deltaLink (太字) の Url にアクセスすることで、更新 (追加・変更・削除) された情報のみを取得できます。
例えば、下記は、recipe.txt を追加し、既存の memo.txt を変更した場合の結果のサンプルです。フォルダ自身と、新規作成された recipe.txt、変更された memo.txt の 3 つのオブジェクトが返されています。
なお、下記の通り、作成と変更の区別は返されないので、アプリケーション側で Id などを元に判断します。

GET https://api.onedrive.com/v1.0/drives('me')/items('9C0AF81B735E29EA!16650')/view.delta?token=aTE09NjM1O...
Accept: application/json
Authorization: Bearer EwBwAq1...
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=none

{
  "@odata.deltaLink": "https://api.onedrive.com/v1.0/drives('me')/items('9C0AF81B735E29EA!16650')/view.delta?token=aTE09NjM1O...",
  "@delta.token": "aTE09NjM1O...",
  "value": [
    {
      "createdBy": {
        "application": {
          "displayName": "OneDrive website",
          "id": "44048800"
        },
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16650",
      "name": "onedrivetest001",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16650",
      "folder": {
        "childCount": 0
      },
      ...

    },
    {
      "@content.downloadUrl": "https://public.bn1304.livefilestore.com/y3mNtAaqVXwBqStACEyc...",
      "createdBy": {
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16656",
      "name": "recipe.txt",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16656",
      "file": {
        "hashes": {
          "crc32Hash": "7DBEF7CA",
          "sha1Hash": "6A381B4E782EC6FFB66745BA9D1371CD0A5A7F15"
        },
        "mimeType": "text/plain"
      },
      ...

    },
    {
      "@content.downloadUrl": "https://public.bn1304.livefilestore.com/y3mWdzbl6NO9uv1f_th_...",
      "createdBy": {
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16653",
      "name": "memo.txt",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16653",
      "file": {
        "hashes": {
          "crc32Hash": "6E7FFD92",
          "sha1Hash": "C7C8EAB6B1F560701BE7F968818DF2DCDCE3E581"
        },
        "mimeType": "text/plain"
      },
      ...

    }
  ]
}

もちろん、アイテムを削除した場合も、その更新内容が取得できます。
例えば、memo.txt を削除して、@data.deltaLink の URL を呼び出すと、下記太字の通り、deleted という要素が付与されて返ってきます。(どのアイテムが削除されたかわかります。)

GET https://api.onedrive.com/v1.0/drives('me')/items('9C0AF81B735E29EA!16650')/view.delta?token=aTE09NjM1...
Accept: application/json
Authorization: Bearer EwBwAq1...
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=none

{
  "@odata.deltaLink": "https://api.onedrive.com/v1.0/drives('me')/items('9C0AF81B735E29EA!16650')/view.delta?token=aTE09NjM1O...",
  "@delta.token": "aTE09NjM1O...",
  "value": [
    {
      "createdBy": {
        "application": {
          "displayName": "OneDrive website",
          "id": "44048800"
        },
        "user": {
          "displayName": "Tsuyoshi Matsuzaki",
          "id": "9c0af81b735e29ea"
        }
      },
      "id": "9C0AF81B735E29EA!16650",
      "name": "onedrivetest001",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16650",
      "folder": {
        "childCount": 0
      },
      ...

    },
    {
      "id": "9C0AF81B735E29EA!16653",
      "name": "memo.txt",
      "webUrl": "https://onedrive.live.com/redir?resid=9C0AF81B735E29EA!16653",
      "deleted": {
        
      },
      "file": {
        
      },
      ...

    }
  ]
}
Comments (0)

Skip to main content