SharePoint Embedded のドキュメントを Microsoft Graph API の itemActivity で追跡すると、同時編集したはずのユーザーが全員出てこないことがあります。本記事では、なぜ起きるのか、仕様としての限界、そして全編集者を漏れなく把握するための現実的な設計パターンを整理します。
結論:itemActivity は「完全な編集者一覧」を保証する仕組みではない
最初に結論から言うと、Microsoft Graph API の /drives/{driveId}/items/{itemId}/activities(itemActivity)は、監査ログの代替ではなく「ユーザー操作をできるだけ反映するアクティビティ フィード」です。特に Word の共同編集(同時編集)で短時間に複数ユーザーが保存・同期するケースでは、すべての編集者が 1:1 で記録されることを前提に設計されていません。
そのため、次の要件を itemActivity 単体 で満たすのは難しいのが実情です。
- 同じタイミングで編集・保存した全ユーザーを漏れなく列挙したい
- 「誰がいつ編集したか」を監査レベルで証跡として残したい
- 後から見ても改ざんに強い履歴として使いたい
一方で、要件が「最近関わった人を参考情報として表示したい」「UI の『アクティビティ』に近い体験を実装したい」程度であれば、itemActivity は十分有用です。大事なのは、期待値(完全性)を正しく置くことです。
現象を整理:Word のバージョン履歴と Graph の itemActivity のズレ
質問で挙がっている現象は、大きく 2 つに分解できます。
- Word(SharePoint)のバージョン履歴に、同時編集した全員が出ない
- Graph の itemActivity にも、全員分のレコードが出ない(例:5 人同時保存でも 2〜3 人分)
これを「Graph がバグっている」と捉える前に、まず 記録の単位 が異なる点を押さえる必要があります。
| 見ているもの | 主な目的 | 記録の単位 | 強み | 限界(ここが誤解されやすい) |
|---|---|---|---|---|
| Word / SharePoint のバージョン履歴 | ファイルの版管理 | 「バージョン」単位 | いつの版に戻せるかが明確 | 1 バージョンに紐づくユーザーは基本的に 1 人(保存・確定の主体)になりやすい |
| Graph itemActivity(/activities) | ユーザー操作のフィード | 「アクティビティ」単位 | 共有・編集・移動など周辺操作も追える | 完全性が保証されない。短時間に集中すると集約・遅延・取りこぼしが起きうる |
| 監査ログ(Purview 等) | 監査・コンプライアンス | 「イベント」単位 | 証跡としての信頼性が高い | 利用できるテナント構成・権限・ライセンスに依存(SharePoint Embedded では制約が出やすい) |
なぜ全員出ないのか:共同編集とアクティビティ記録の“噛み合わなさ”
itemActivity は「ベストエフォート」になりやすい理由
共同編集では、ユーザーの操作は「編集(キーストローク)→自動保存→同期→競合解決→サーバー側で統合」という流れで細かく発生します。しかし、アクティビティ フィードがそれを そのままの粒度 で全件保持すると、次の問題が起きます。
- イベント数が爆発し、保管コストも取得コストも高すぎる
- UI 的にも「数秒ごとに同じ人の edit が並ぶ」ノイズになる
- 短時間の連続操作は、意味的には「編集した」の一言で十分なことが多い
このため一般に、アクティビティ フィードは 一定時間内の操作をまとめる、重複を抑制する、内部処理のタイミングで遅れて出てくる といった性質を持ちます。結果として「同時刻に 5 人が保存したのに 2〜3 人しか見えない」状況が起きえます。
Word / SharePoint のバージョン履歴が「一つのバージョンに一人が紐づきやすい」理由
バージョン履歴は、あくまで「版(スナップショット)」を管理する仕組みです。共同編集の世界では、複数人の編集が 1 つの最新版に統合されるため、最終的にサーバー側で「この版を確定させたのは誰か」を 1 人に寄せて表現しがちです。
とくに次のような条件が重なると、「同じ時間帯に触っていた全員」がバージョン履歴に出ないことがあります。
- 自動保存により、ユーザー個別の「保存」概念が薄い(常に同期され続ける)
- バージョンが作られるタイミングが「一定時間ごと」「終了時」「チェックイン時」などに偏る
- 同時編集の統合処理で、最後に確定させたユーザーが代表として残りやすい
ここで重要なのは、これは「誰がどれだけ編集したか」を記録する設計ではなく、「どの版が存在するか」を管理する設計だという点です。つまり、バージョン履歴に対して「同時編集者の完全一覧」を期待すると、どうしてもズレが出ます。
取りこぼしに見える典型パターン(実装側の落とし穴も含む)
実際の現場では、仕様上の集約だけでなく、クライアント実装の都合で「出ていない」と誤認するケースも混ざります。まずは次の表で、よくあるパターンを潰しておくと原因切り分けが速くなります。
| 起きていること | よくある原因 | まず試す確認 |
|---|---|---|
| 少人数しか返ってこない | $top の既定値が小さく、ページングしていない | @odata.nextLink があるか確認し、最後まで追う |
| 直後に叩くと出ないが、しばらく後だと出る | 内部処理の遅延、集約、最終確定のタイミング差 | 編集終了直後だけでなく、数分〜十数分の幅で再取得して差分を見る |
| 特定のユーザーだけ出ない | そのユーザーの操作が「版の確定」に寄与していない/集約で代表から外れている | 同時編集の流れ(誰が最後に閉じたか、どのクライアントか)を記録して照合する |
| Word の履歴と activities の人物が一致しない | そもそも記録単位が違う(版管理 vs フィード) | 「同じ答えが返るべき」と思い込まず、目的に合うログを選び直す |
Graph itemActivity を使うなら押さえるべき取得設計
「完全な監査」はできなくても、itemActivity を使って 取りこぼしを最小化する設計はできます。ポイントは、API の戻りを「その瞬間の完全リスト」と見なさず、時間幅を持ったフィードとして扱うことです。
基本の取得例(ページング前提)
エンドポイントは次の形です。実装では必ずページングを考慮してください。
GET https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}/activities?$top=50
Authorization: Bearer {access_token}
レスポンスは環境により異なりますが、概念的には「誰(actor)が、いつ(times)その操作(action)をしたか」を表します。
{
"value": [
{
"actor": { "user": { "id": "…", "displayName": "…"} },
"times": { "recordedDateTime": "2025-12-01T10:00:00Z" },
"action": { "edit": {} }
}
],
"@odata.nextLink": "https://graph.microsoft.com/…"
}
取得の実務ポイント
- 必ず全ページ取得する:
@odata.nextLinkがある限り追う。途中で打ち切ると「人数が少ない」状態を自作してしまいます。 - 取得タイミングを分散する:編集直後・数分後・一定間隔など、時間差で取得し、ユニークユーザーを統合する。
- ユニーク化は「ユーザーID+時間帯」で:同一人物が複数回出るのは正常です。表示用途なら「最新日時で上書き」などのルールを持つ。
- “編集した”の定義を明確に:テキスト変更だけなのか、コメント追加・共有・リネームも含むのか。定義によって見るべき action が変わります。
全編集者を漏れなく取得したい場合の選択肢
「同時編集した全ユーザーを必ず列挙したい」という要件は、フィードではなく 監査・テレメトリ設計の領域です。ここでは、現実的に取りうる手段を“期待できる精度”ごとに並べます。
| 手段 | 期待できる網羅性 | 導入コスト | SharePoint Embedded での注意 | 向いている用途 |
|---|---|---|---|---|
| Graph itemActivity(/activities) | 中(参考情報) | 低 | 完全な共同編集者一覧は狙わない | 最近操作したユーザーの表示、アクティビティUI |
| バージョン履歴(Graph の versions など) | 低〜中(代表者中心) | 中 | 1 バージョンに複数ユーザーが紐づく形にはなりにくい | 版管理、差分の参照、復元 |
| Purview 監査ログ(統合監査ログ) | 高(監査向け) | 中〜高 | テナント構成・権限・提供形態により “ISV 側が直接参照できない” ことがある | コンプライアンス、監査、インシデント調査 |
| アプリ側ロギング(編集セッションログ) | 高(設計次第) | 中〜高 | Embedded ではここが現実解になりやすい | 「誰が参加していたか」を確実に残す、リアルタイム表示 |
| 運用ルール(同時保存を減らす) | 状況改善 | 低 | 技術だけでなく運用も合わせると事故が減る | 重要文書の取り扱い、変更管理 |
Purview 監査ログが使えるなら、まずそれが最短ルート
「完全性」が必要なら、本来は監査ログの領域です。SharePoint / OneDrive / Office の操作は、監査目的のログとして集約できる仕組みが用意されています。もし利用できるテナント・権限・契約であれば、編集・アクセス・共有といったイベントをより高い信頼性で追跡できます。
ただし SharePoint Embedded の構成では、データが存在するテナント(Consuming Tenant)と、アプリ提供者(Provider)が異なるため、提供者側が自由に監査ログへアクセスできないなどの制約が出がちです。この場合は「顧客側が監査ログを保持し、必要に応じて連携する」という設計が現実的です。
SharePoint Embedded で現実的:アプリ側で“編集セッション”を記録する
共同編集の「参加者」を確実に捕まえるには、サーバー側のフィードではなく、アプリが編集体験を提供する時点でログを取るのが強力です。例えば次のように、ユーザーがドキュメントを開いた瞬間から閉じるまでを 1 セッションとして記録します。
| ログするイベント | 記録する項目の例 | 狙い |
|---|---|---|
| 編集開始(open) | ユーザーID、ドキュメントID、開始時刻、クライアント種別(Word/ブラウザ)、セッションID | 「参加者」を漏れなく捕捉する |
| ハートビート(heartbeat) | セッションID、最終生存時刻、IP/デバイス情報(必要最小限) | 異常終了でも在席を推定できる |
| 保存トリガー(save) | セッションID、保存時刻、保存種別(自動/手動の推定) | 「いつ保存したか」を近似的に残す |
| 編集終了(close) | セッションID、終了時刻、滞在時間 | 監査・課金・利用分析にも使える |
保存トリガーは Word クライアントの内部イベントを 100% 捕捉できないこともありますが、少なくとも「同時編集していた全員(参加者)」は open/heartbeat で確実に掴めます。要件が「編集した人」ではなく「編集に参加した人」まで含むのであれば、むしろこちらの方が目的に合うケースが多いです。
データモデルは、例えば次のようなテーブル設計にすると扱いやすくなります。
-- 例:編集セッションを記録するテーブル(概念)
EditorSession
- sessionId (PK)
- documentId (driveId + itemId などで一意化)
- userId
- startedAt
- lastSeenAt
- endedAt (NULL 可)
- clientType (WordDesktop / WordWeb / Mobile など)
- correlationId (任意。調査用)
そして画面側では、指定した時間帯(例:過去 24 時間)にセッションを持つユーザーを集計し、「最近編集したユーザー」や「同時編集者」を表示します。Graph の itemActivity は補助情報として添える、という役割分担にすると安定します。
“完全な編集者”の定義を分解して要件を満たす
「編集者を漏れなく取得したい」という要望は、実は複数の要件が混ざっていることが多いです。設計に入る前に、まず定義を分解すると実装の落とし所が見つかります。
| よくある言い方 | 実際に欲しい情報 | おすすめの取り方 |
|---|---|---|
| 最近編集した人 | 直近の関与者(参考) | itemActivity を時間幅で集計+ページング徹底 |
| 同時編集した全員 | 編集セッションに参加していたユーザー一覧 | アプリ側の open/heartbeat ログ(参加者ログ) |
| 誰がいつどこを変えたか | 改ざん防止を含む監査証跡 | Purview 監査ログ(可能なら)+管理プロセス |
| 保存した人 | 版を確定させた主体 | バージョン履歴(代表者が中心になることを許容) |
どうしても Graph だけで近づけたいときの“合わせ技”
制約上、Purview やアプリ側ロギングをすぐ導入できない場合、Graph の複数情報を組み合わせて「それっぽく」近づけることはできます。ただし、これは100% を保証する方法ではありません。
組み合わせ例
- itemActivity:編集・共有・コメントなど「関与」の幅を拾う
- DriveItem のメタデータ:
lastModifiedByとlastModifiedDateTimeで直近の代表者を押さえる - バージョン履歴:版の確定者を押さえる(復元要件にも効く)
- 変更通知(Webhook / Delta 同期):更新タイミングを捕捉し、取り込みジョブのトリガーにする
実装イメージとしては、「変更通知を受け取ったら、対象ファイルの itemActivity を時間幅で取得して、自前テーブルに追記する」というパイプラインになります。フィードの取りこぼしがゼロにはならなくても、“見えなかった”を減らす方向には効きます。
仕様かバグか:判断の目安と、サポートに出すときの準備
同時編集時の欠落は、アクティビティの性質上「仕様に近い挙動」と考えるのが安全です。ただし、特定の条件で 極端に 欠落する、同じ操作で再現率が高い、明らかに API が誤った actor を返す、といった場合は不具合の可能性もあります。
サポートにエスカレーションする際に揃える情報
Microsoft へ問い合わせる場合、再現性のある情報があるほど調査が進みやすくなります。次をセットで残す運用をおすすめします。
- 対象の
driveId/itemId(可能ならファイル名・拡張子も) - 同時編集したユーザーの一覧(UPN など)と、各ユーザーの操作時刻(できれば UTC)
- クライアント情報(Word デスクトップ / Web / モバイル、バージョン)
- Graph 呼び出しのリクエスト(URL・クエリ・ヘッダー)とレスポンス全文
@odata.nextLinkを追ったかどうか(追った場合は全ページのログ)- アプリ側で取得できる相関 ID(correlationId / request-id 等)
特に、同時編集の再現実験では「誰が最後に閉じたか」「最後に回線断したのは誰か」などが結果に影響することがあります。操作手順を文章で残すだけでなく、可能なら画面録画や操作ログ(アプリ側ログ)も添えると強いです。
実務で効く対処アイデア:要件を落とさずに“壊れにくい”設計にする
「最近編集したユーザー表示」はスコアリングで安定させる
UI に出したいのが「最近編集した人」の場合、完全性よりも安定性が大切です。例えば次のようにスコアリングすると、フィードの揺らぎを吸収できます。
- 直近 24 時間の itemActivity を集計し、ユーザーごとに最新時刻を採用
- 同一ユーザーの edit/comment/share を重み付けして「関与度」を算出
- 最後に
lastModifiedByが示すユーザーを少し優先する
こうすると、活動が多いときに一部の edit が欠けても「最近関与した人」のランキングは大きく崩れにくくなります。
監査要件があるなら、ログの責任範囲を最初に決める
SharePoint Embedded を ISV として提供する場合、監査ログの責任範囲(誰が保持し、誰が参照できるか)は後から揉めやすいポイントです。次のどちらかに寄せると整理しやすくなります。
- 顧客テナント責任:Purview 監査ログは顧客が保持。必要に応じてエクスポート/共有を受ける
- アプリ責任:アプリ側で編集セッションログを保持し、顧客へ監査ビューを提供する
中途半端に「Graph の /activities で監査できます」と言ってしまうと、同時編集で欠落した瞬間に信頼を失いやすいので注意してください。
運用ルールで事故を減らす(技術とセットが最強)
重要文書では、技術だけでなく運用も合わせると効果が大きいです。
- 短時間に大量の共同編集が発生するテンプレート文書は、編集者をロールで制限する
- 確定前は共同編集、確定時は担当者が「確定保存」するなど、版管理のルールを作る
- 監査対象のドキュメントは、編集後に承認フロー(レビュー)を必須化する
まとめ:狙うべきゴールを変えると解決が早い
- Graph の itemActivity は便利だが、共同編集の全員を漏れなく列挙する用途には向きにくい
- Word / SharePoint のバージョン履歴も、版管理の仕組みであり「同時編集者一覧」を完全に復元するものではない
- 監査レベルの証跡が必要なら、Purview 等の監査ログの活用を前提に要件を組む
- SharePoint Embedded で監査ログ利用が難しい場合は、アプリ側で編集セッションログを設計し、参加者を確実に捕捉する
- どうしても Graph 中心で進めるなら、/activities を時間幅で集計し、ページング・遅延・重複を前提に扱う

コメント