Box User Event: REST APIとPython

Yuko Taniguchi
Box Developer Japan Blog
11 min readOct 3, 2023
Image By vecstock

Box Platformでは、アプリケーションがサブスクライブできるイベントを公開します。これらのイベントは、標準ユーザーまたはサービスユーザーによって実行される処理に関連しています。

イベントは、User Event (単一のユーザーのセキュリティコンテキストでトリガーされるイベント) またはEnterprise Event (企業のコンテキスト内でユーザーまたはコンテンツに関連するイベント) 別にまとめられています。

この記事では、User Eventに重点を置いて説明します。

以下のさまざまなストリームタイプをサブスクライブできます。

  • all — ユーザーに関するすべてのイベント
  • changes — コンテンツの変更
  • sync — 同期対象フォルダに関連した変更

User Events

現在認証されているユーザーに関連する、低レイテンシのイベントストリームを提供します。

アプリケーションではこれを利用して、Boxアカウントに影響を与えた、アプリによる処理をユーザーに通知できます。

通常、/eventsエンドポイントへのアクセスは次のようになります。

curl --location 'https://api.box.com/2.0/events?stream_type=all' \
--header 'Authorization: Bearer Q0...ZE'

以下が返されます。

{
"entries": [
{
"type": "event",
"event_id": "0a0dbedf101e537a6a1252924a7da9d966d91745",
"created_by": {
"type": "user",
"id": "18622116055",
"name": "Rui Barbosa",
"login": "..."
},
"created_at": "2023-06-20T06:58:26-07:00",
"recorded_at": "2023-06-20T06:58:27-07:00",
"event_type": "ITEM_CREATE",
"session_id": "37s0swhf516mcne1",
"source": {
"type": "folder",
"id": "213534631621",
...
}
}
],
"chunk_size": 1,
"next_stream_position": 30400984754258517
}

Python SDKを使用した同様のイベントクエリ:

def main():
"""
Simple script to demonstrate the use of events
"""
client = get_client(conf)
stream_position = 0
events = client.events().get_events(
limit=10,
stream_position=stream_position,
stream_type="all"
)
stream_position = events["next_stream_position"]
for event in events["entries"]:
print(
f"Got {event.event_type} on {event.created_at}: {event.source.type} {event.source.id} {event.source.name}"
)

この出力結果は以下のとおりです。

Got ITEM_UPLOAD on 2023-07-10T05:39:35-07:00: file 1256595598279 IMG_20190406_160049.jpg
Got ITEM_TRASH on 2023-07-10T05:40:27-07:00: file 1204688948039 Get Started with Box.pdf
Got ITEM_PREVIEW on 2023-07-10T05:40:41-07:00: file 1256595598279 IMG_20190406_160049.jpg
Got ITEM_TRASH on 2023-07-10T05:40:54-07:00: file 1256595598279 IMG_20190406_160049.jpg
Got ITEM_UNDELETE_VIA_TRASH on 2023-07-10T05:41:02-07:00: file 1204688948039 Get Started with Box.pdf

この呼び出しによってnext_stream_positionがどのように返されるかに注目してください。中断した場所から続行できるようにstream_positionとして送り返すことができます。

Long Polling

User EventはLong pollingもサポートしています。つまり、接続が開いたままのAPIに対してgetを実行できます。イベントがトリガーされると、レスポンスが返されます。

ウェブソケット接続を開いていないことに注意してください。

これは2つのステップからなるプロセスです。

まず、/eventsエンドポイントに対してOPTIONSリクエストを送信すると、Long pollingに必要なURLが返されます。

返されたURLに対してGETを送信すると、イベントが送信されるまで接続は開いたままになります。以下に例を示します。

curl --location --request OPTIONS 'https://api.box.com/2.0/events' \
--header 'Authorization: Bearer D8...sF'

結果は次のとおりです。

{
"chunk_size": 1,
"entries": [
{
"type": "realtime_server",
"url": "https://2.realtime.services.box.net/subscribe?channel=b188ef734f6f71fa6c85&stream_type=all",
"ttl": "10",
"max_retries": "10",
"retry_timeout": 610
}
]
}

次に、返されたURLをGETで取得します。

curl --location 'https://2.realtime.services.box.net/subscribe?channel=b188ef734f6f71fa6c85&stream_type=all' \
--header 'Authorization: D8...sF'

これで、イベントがトリガーされたに次の結果が返されます。

{
"message": "new_change",
"source": "uesi"
}

ご覧のとおり、レスポンスには何も情報がありません。これは、何か新しいことが起こったことをクライアントに通知しているだけです。ここで、クライアントは、新しいイベントを取得するために、最新の既知のstream_positionを指定して/eventsエンドポイントにクエリを実行する必要があります。

curl --location 'https://api.box.com/2.0/events?stream_type=all&stream_position=30400986485844810&limit=50' \
--header 'Authorization: Bearer D8...sF'
{
"entries": [
{
"type": "event",
"event_id": "4f68ef94037862ddaadb74ff33e8d2758cf9a131",
...
"source": {
"type": "file",
"id": "1256652018464",
}
},
{
"type": "event",
"event_id": "5fb318e46a7002104cb7db2223729b1b724ed294",
...
"source": {
"type": "file",
"id": "1256624574539",
}
}
],
"chunk_size": 2,
"next_stream_position": 30400986485908488
}

上記の例では、2つのファイルをごみ箱に移動しました。

この時点で、クライアントは、/eventsエンドポイントにOPTIONSを送信して新しいURLを取得し、そのURLにGETを送信することでプロセスを再開し、新しいイベントを受け取るまで待機する必要があります。

{
"chunk_size": 1,
"entries": [
{
"type": "realtime_server",
"url": "https://2.realtime.services.box.net/subscribe?channel=b188ef734f6f71fa6c85&stream_type=all",
"ttl": "10",
"max_retries": "10",
"retry_timeout": 610
}
]
}

OPTIONSのレスポンスにはttlmax_retriesretry_timeoutが含まれていることに注意してください。これらのいずれかが発生した場合は、アプリは再度OPTIONSで開始する必要があります。

SDKを使用したLong polling

Box SDKのいずれかを使用すれば、実装はさらに簡単になります。

次のPythonスクリプトを考えてみましょう。

def main():
"""
Simple script to demonstrate the use of events
"""

client = get_client(conf)

print("Long polling for events...")
events = client.events().generate_events_with_long_polling()
for event in events:
print(
"\t",
event.created_at,
event.event_type,
event.source.type,
event.source.id,
event.source.name,
)

アプリの一部のファイルを削除すると、次のようになります。

Long polling for events...
2023-07-10T08:16:39-07:00 ITEM_TRASH file 1256624574539 IMG_20190406_160049.jpg
2023-07-10T08:16:38-07:00 ITEM_TRASH file 1256652018464 box-dev-logo (1).png

新しいOPTIONSの送信と最新のストリーム位置の管理はすべて、SDKで行われることに注意してください。基本的に、generate_events_with_long_pollingメソッドでは、new_changeメッセージを受信するたびに次の一連のイベントをポーリングし続けるジェネレータが返されます。

その他の考慮事項

Boxでのイベントの保存は無期限ではありません。

User Eventの保存期間は2週間~2か月間で、その後、User Eventは削除されます。

このフィードでは、すべての結果を迅速に返すことを重視しています。つまり、Boxでは、イベントが順不同で複数回返される可能性があります。重複するイベントは、イベントIDで識別できます。

APIリクエストでUser Eventにフィルタをかけることはできませんが、Enterprise Eventではサポートされています。

この記事を楽しんでいただけたら、ぜひ [Clap] をクリックしてください。

質問がある場合は、この記事のコメント欄またはBoxのforum.box.com (英語のみ) に投稿してください。

上記の例は、こちらのGitHubリポジトリを使用して簡単に複製できます。

Box Platformの使用を開始する場合は、こちらのGitHubリポジトリにあるPythonアプリのテンプレートをご確認ください。

--

--