# 機能設計書 34-チャット履歴取得

## 概要

本ドキュメントは、Etherpadのチャット履歴取得機能（getChatHistory）の設計仕様を記載する。

### 本機能の処理概要

本機能は、指定されたパッドのチャット履歴を取得するAPI機能である。開始位置と終了位置を指定することで、部分的な履歴取得も可能。

**業務上の目的・背景**：Etherpadはリアルタイム共同編集機能に加えて、チャット機能を提供している。この機能は、パッドに関連付けられたチャットメッセージの履歴を取得することで、過去のコミュニケーション内容を確認したり、外部システムへのエクスポートを可能にする。特にパッドを開いた際に過去のチャットを表示するために使用される。

**機能の利用シーン**：パッド編集画面でチャットウィンドウを開いた際の履歴読み込み、チャット履歴のアーカイブ・エクスポート、管理画面からのチャット内容確認など。

**主要な処理内容**：
1. パッドIDと開始・終了位置の検証
2. パッドオブジェクトの取得
3. 指定範囲のチャットメッセージを取得
4. 各メッセージに著者名を付加
5. メッセージ配列を返却

**関連システム・外部連携**：REST API経由でHTTPリクエストを受け付け、JSON形式でレスポンスを返す。Socket.IO経由でクライアントからも呼び出される（GET_CHAT_MESSAGESメッセージ）。

**権限による制御**：APIキー認証またはSSO認証により、API呼び出しの認可を行う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 主画面 | チャットウィンドウにチャット履歴を表示 |

## 機能種別

データ取得（SELECT操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | 対象パッドのID | 文字列型であること、パッドが存在すること |
| start | integer | No | 取得開始位置（チャットメッセージID） | 0以上であること |
| end | integer | No | 取得終了位置（チャットメッセージID） | start以上、chatHead以下であること |

### 入力データソース

REST API GETリクエストのクエリパラメータとしてパラメータを受け取る。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | integer | 結果コード（0: 成功） |
| message | string | 結果メッセージ（"ok"） |
| data.messages | array | チャットメッセージの配列 |
| data.messages[].text | string | メッセージ本文 |
| data.messages[].authorID | string | 送信者の著者ID |
| data.messages[].time | integer | 送信時刻（ミリ秒エポック） |
| data.messages[].userName | string | 送信者の表示名 |

### 出力先

HTTPレスポンスとしてJSON形式で返却

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ GET /api/2/pads/chatHistory?padID=xxx&start=0&end=100
2. 認証・認可チェック
   └─ APIキーまたはSSOトークンの検証
3. パラメータ検証
   └─ start, endの範囲検証
4. パッド存在確認・取得
   └─ padManager.getPadを呼び出し
5. 範囲パラメータのデフォルト設定
   └─ start/endが未指定の場合は全履歴を対象
6. 範囲検証
   └─ start/endがchatHeadを超えていないか確認
7. チャットメッセージ取得
   └─ pad.getChatMessages(start, end)を呼び出し
8. レスポンス生成・返却
   └─ {messages: [...]}形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{認証チェック}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D{start/end検証}
    D -->|start < 0| E[start is below zero]
    D -->|end < 0| F[end is below zero]
    D -->|start > end| G[start is higher than end]
    D -->|正常| H{パッド存在確認}
    H -->|存在しない| I[padID does not exist]
    H -->|存在する| J{範囲パラメータ設定}
    J --> K{chatHead超過確認}
    K -->|start > chatHead| L[start is higher than chatHead]
    K -->|end > chatHead| M[end is higher than chatHead]
    K -->|正常| N[チャットメッセージ取得]
    N --> O[JSONレスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-34-01 | パッド存在必須 | 指定されたパッドIDが存在しない場合はエラー | 常時 |
| BR-34-02 | 範囲デフォルト | start/endが未指定の場合は全履歴（0〜chatHead）を取得 | パラメータ未指定時 |
| BR-34-03 | 範囲制限 | start/endはchatHead以下である必要がある | 常時 |
| BR-34-04 | 著者名付加 | 各メッセージにはauthorManagerから取得した著者名が付加される | 常時 |
| BR-34-05 | 壊れたエントリのスキップ | 存在しないチャットエントリはスキップされる | データ不整合時 |

### 計算ロジック

チャットメッセージの取得は閉区間（inclusive）で行われる。つまりstart=0, end=10の場合、0から10までの11件のメッセージが取得される。

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パッド情報取得 | pad:{padID} | SELECT | パッドのメタ情報（chatHead）を取得 |
| チャット取得 | pad:{padID}:chat:{n} | SELECT | 各チャットメッセージを取得 |
| 著者名取得 | globalAuthor:{authorID} | SELECT | 著者名を取得 |

### テーブル別操作詳細

#### pad:{padID}:chat:{n}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | text, authorId, time | キー: pad:{padID}:chat:{n}（nはstart〜end） | 並列取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | apierror | startが0未満 | 0以上の値を指定 |
| 1 | apierror | endが0未満 | 0以上の値を指定 |
| 1 | apierror | startがendより大きい | start <= endとなる値を指定 |
| 1 | apierror | startがchatHead超過 | chatHead以下の値を指定 |
| 1 | apierror | endがchatHead超過 | chatHead以下の値を指定 |
| 1 | apierror | パッドが存在しない | 存在するパッドIDを指定 |
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを使用 |

### リトライ仕様

リトライは不要（冪等な読み取り操作のため）

## トランザクション仕様

読み取り専用操作のため、トランザクション管理は不要。

## パフォーマンス要件

- レスポンス時間: メッセージ数により変動
  - 100件以内: 200ms以内
  - 1000件以上: 1秒以上かかる場合あり
- 並列取得によりI/O効率を最適化

## セキュリティ考慮事項

- APIキー認証またはSSO認証が必須
- パッドIDのバリデーションにより不正なアクセスを防止
- 読み取り専用操作のためデータ改ざんリスクなし

## 備考

API Version 1.2.7で追加された機能。Socket.IO経由のGET_CHAT_MESSAGESメッセージでも同様の機能が提供される。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、ChatMessageクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ChatMessage.ts | `src/static/js/ChatMessage.ts` | ChatMessageクラスの定義 |

**読解のコツ**: ChatMessageはtext, authorId, time, displayNameを持つシンプルなデータクラス。

#### Step 2: エントリーポイントを理解する

REST APIのエンドポイント定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | GET /pads/chatHistoryのマッピング定義（921-956行目） |

**主要処理フロー**:
1. **921-956行目**: エンドポイント定義とgetChatHistory関数へのマッピング

#### Step 3: API関数の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | getChatHistory関数の実装（329-364行目） |

**主要処理フロー**:
- **329-340行目**: パラメータ検証（start, endの範囲チェック）
- **343行目**: パッド取得
- **345-351行目**: デフォルト値設定とchatHead確認
- **353-358行目**: chatHead超過チェック
- **361行目**: getChatMessages呼び出し

#### Step 4: Padクラスのチャット取得を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Pad.ts | `src/node/db/Pad.ts` | getChatMessages()メソッド（366-380行目） |
| 4-2 | Pad.ts | `src/node/db/Pad.ts` | getChatMessage()メソッド（351-357行目） |

**主要処理フロー**:
- **351-357行目**: 単一メッセージ取得と著者名付加
- **366-380行目**: 並列取得と壊れたエントリのフィルタリング

### プログラム呼び出し階層図

```
RestAPI.expressCreateServer
    │
    ├─ app.use('/api/2', ...)
    │      └─ apiHandler.handle('1.2.7', 'getChatHistory', fields)
    │              └─ API.getChatHistory(padID, start, end)
    │                      ├─ getPadSafe(padID, true)
    │                      └─ pad.getChatMessages(start, end)
    │                              ├─ Stream.range(start, end+1).map()
    │                              └─ getChatMessage(entryNum)
    │                                      ├─ db.get(`pad:${id}:chat:${entryNum}`)
    │                                      └─ authorManager.getAuthorName(authorId)
    │
    └─ res.json({messages})
```

### データフロー図

```
[入力]                       [処理]                         [出力]

padID ─────────────────────▶ getPadSafe() ───────────────▶ Padオブジェクト
                                     │
start, end ─────────────────▶ 範囲検証
                                     │
                                     ▼
                          getChatMessages(start, end)
                                     │
                                     ▼
pad:{padID}:chat:{n} ────▶ db.get() ─────────────────────▶ raw message
                                     │
                                     ▼
globalAuthor:{authorId} ─▶ getAuthorName() ──────────────▶ displayName
                                     │
                                     ▼
                          ChatMessage[] ──────────────────▶ {messages: [...]}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | getChatHistory関数の実装 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | getChatMessages/getChatMessageメソッド |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | REST APIエンドポイント定義 |
| ChatMessage.ts | `src/static/js/ChatMessage.ts` | ソース | ChatMessageクラスの定義 |
| AuthorManager.ts | `src/node/db/AuthorManager.ts` | ソース | 著者名取得処理 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | Socket.IO経由のチャット取得処理 |
