# 機能設計書 21-リビジョン変更セット取得

## 概要

本ドキュメントは、Etherpadにおけるリビジョン変更セット取得機能の設計を定義するものである。

### 本機能の処理概要

この機能は、指定されたパッドの特定リビジョンにおける変更セット（Changeset）をJSON形式で取得する機能である。変更セットはEtherpadの差分表現形式であり、テキストの挿入・削除・属性変更を効率的に記録した文字列である。

**業務上の目的・背景**：タイムスライダー機能や履歴表示機能において、特定のリビジョン時点での変更内容を確認する必要がある。また、外部システムとの連携や監査ログの取得、パッドの履歴分析などの用途にも使用される。変更セットはEtherpadの内部差分形式であり、これを取得することでパッドの詳細な編集履歴を追跡できる。

**機能の利用シーン**：
- タイムスライダー画面でのリビジョン間差分の計算
- パッドの編集履歴の詳細分析
- 外部システムへのパッド変更履歴のエクスポート
- カスタムプラグインでの変更追跡機能の実装

**主要な処理内容**：
1. リクエストパラメータのバリデーション（padID、rev）
2. パッドの存在確認と取得
3. リビジョン番号の妥当性チェック（revパラメータが未指定の場合は最新リビジョンを使用）
4. 指定リビジョンの変更セットをデータベースから取得
5. 変更セット文字列をJSON形式で返却

**関連システム・外部連携**：
- ueberDB2データベース層を経由してリビジョンデータを取得
- REST API（`GET /api/2/pads/revisionChangeset`）として外部システムから呼び出し可能

**権限による制御**：API認証（APIキーまたはOAuth2トークン）が必要。パッドへのアクセス権限は本機能では明示的にチェックされないが、APIレベルでの認証は必須。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | タイムスライダー画面 | 主画面 | 各リビジョンの変更セットを取得してスライダー再生 |

## 機能種別

データ取得（SELECT）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | パッドの識別子 | 文字列型であること、有効なパッドIDであること |
| rev | string/number | No | 取得対象のリビジョン番号 | 整数に変換可能であること、0以上かつヘッドリビジョン以下であること |

### 入力データソース

- HTTPリクエストのクエリパラメータ（GETリクエスト）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | integer | レスポンスコード（0: 成功） |
| message | string | レスポンスメッセージ（"ok"） |
| data | string | 変更セット文字列（例: "Z:1>6b|5+6b$Welcome to Etherpad!..."） |

### 出力先

- HTTPレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. APIリクエストの受信
   └─ GET /api/2/pads/revisionChangeset?padID=xxx&rev=yyy

2. 入力パラメータのバリデーション
   └─ revパラメータが指定されている場合は数値に変換

3. パッドの取得
   └─ getPadSafe()でパッドIDの妥当性確認とパッド取得

4. リビジョン番号の決定
   ├─ revが指定されている場合: 指定値を使用
   └─ revが未指定の場合: ヘッドリビジョン番号を使用

5. リビジョン妥当性チェック
   └─ revがヘッドリビジョンを超える場合はエラー

6. 変更セットの取得
   └─ pad.getRevisionChangeset(rev)でデータベースから取得

7. レスポンスの返却
   └─ 変更セット文字列をJSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[リクエスト受信]
    B --> C{revパラメータあり?}
    C -->|Yes| D[revを数値に変換・検証]
    C -->|No| E[rev=undefined]
    D --> F[パッド取得 getPadSafe]
    E --> F
    F --> G{パッド存在?}
    G -->|No| H[エラー: padID does not exist]
    G -->|Yes| I[ヘッドリビジョン取得]
    I --> J{revが指定されている?}
    J -->|Yes| K{rev > head?}
    J -->|No| L[rev = head]
    K -->|Yes| M[エラー: rev is higher than head]
    K -->|No| N[getRevisionChangeset実行]
    L --> N
    N --> O[変更セットを返却]
    O --> P[終了]
    H --> P
    M --> P
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-21-01 | リビジョン範囲制限 | 取得可能なリビジョンは0からヘッドリビジョンまで | 常に適用 |
| BR-21-02 | デフォルトリビジョン | rev未指定時は最新リビジョンの変更セットを返却 | revパラメータがundefined時 |
| BR-21-03 | パッド存在確認 | 存在しないパッドIDの場合はエラーを返却 | 常に適用 |

### 計算ロジック

変更セットは以下の形式でエンコードされている:
- `Z:` - 変更セットマジック
- `oldLen>newLen` - 文字数の変化（例: `1>6b` = 1文字から107文字への変化）
- `|行数+文字数` - 挿入操作の詳細
- `$...` - 挿入されるテキスト

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| リビジョン取得 | pad:{padID}:revs:{revNum} | SELECT | 指定リビジョンの変更セットを取得 |

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

#### pad:{padID}:revs:{revNum}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | changeset | キー: pad:{padID}:revs:{revNum}, サブキー: changeset | ueberDB2のgetSub使用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| apierror | パッド不存在 | 指定されたpadIDが存在しない | 正しいpadIDを指定する |
| apierror | リビジョン範囲超過 | revがヘッドリビジョンより大きい | 有効なリビジョン番号を指定する |
| apierror | 無効なリビジョン | revが数値でない、または負数 | 有効な整数を指定する |

### リトライ仕様

特になし（読み取り専用操作のため）

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

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

## パフォーマンス要件

- レスポンス時間: 通常100ms以内
- 変更セットはリビジョンごとにデータベースに保存されているため、単一キーの読み取りで完了

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

- API認証（APIキーまたはOAuth2トークン）が必須
- パッドIDはサニタイズされる（padManager.sanitizePadId）
- 変更セット自体にはセンシティブな情報が含まれる可能性があるため、適切なアクセス制御が必要

## 備考

- 変更セットはEtherpad固有の差分表現形式であり、解析には専用のパーサーが必要
- タイムスライダー機能ではこのAPIを使用して履歴を再生する

---

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

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

### 推奨読解順序

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

まず、変更セット（Changeset）の構造と、リビジョンデータの格納形式を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadType.ts | `src/node/types/PadType.ts` | AChangeSet, AText等の型定義を確認 |
| 1-2 | Changeset.ts | `src/static/js/Changeset.ts` | 変更セットのパース・生成ロジック |

**読解のコツ**: 変更セットは`Z:oldLen>newLen|lines+chars$text`形式でエンコードされている。

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

API呼び出しの起点となるファイルを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | APIルーティング定義（1011-1032行目）|
| 2-2 | APIHandler.ts | `src/node/handler/APIHandler.ts` | APIバージョン管理とパラメータマッピング（89-93行目）|

**主要処理フロー**:
1. **1011-1032行目** (RestAPI.ts): `/pads/revisionChangeset`エンドポイントの定義
2. **92行目** (APIHandler.ts): getRevisionChangeset関数のパラメータ定義 `['padID', 'rev']`

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

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

**主要処理フロー**:
- **127-131行目**: 関数定義とrevパラメータのバリデーション
- **134-135行目**: パッド取得とヘッドリビジョン取得
- **138-145行目**: revパラメータ指定時の処理とバリデーション
- **149行目**: 変更セットの取得と返却

#### Step 4: Padクラスのメソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Pad.ts | `src/node/db/Pad.ts` | getRevisionChangeset メソッド（168-171行目）|

**主要処理フロー**:
- **168-171行目**: データベースから変更セットを取得（getSub使用）

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

```
RestAPI.expressCreateServer (RestAPI.ts)
    │
    ├─ Express Router: GET /api/2/pads/revisionChangeset
    │      │
    │      └─ APIHandler.handle (APIHandler.ts)
    │             │
    │             └─ API.getRevisionChangeset (API.ts:127-150)
    │                    │
    │                    ├─ checkValidRev (checkValidRev.ts)
    │                    │
    │                    ├─ getPadSafe (API.ts:894-920)
    │                    │      │
    │                    │      └─ padManager.getPad (PadManager.ts)
    │                    │             │
    │                    │             └─ Pad.init (Pad.ts)
    │                    │
    │                    └─ Pad.getRevisionChangeset (Pad.ts:168-171)
    │                           │
    │                           └─ db.getSub (ueberDB2)
```

### データフロー図

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

padID ─────────────────> getPadSafe ─────────────────> Pad Object
                              │
rev (optional) ──────────> checkValidRev ──────────> Validated Rev Number
                              │
                              v
                    getRevisionChangeset
                              │
                              v
                    db.getSub('pad:{id}:revs:{rev}', ['changeset'])
                              │
                              v
                    ───────────────────────────────> Changeset String
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | getRevisionChangeset関数の実装 |
| APIHandler.ts | `src/node/handler/APIHandler.ts` | ソース | APIバージョン管理とパラメータ定義 |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | RESTエンドポイント定義 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | Padクラスとリビジョン取得メソッド |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理とキャッシュ |
| checkValidRev.ts | `src/node/utils/checkValidRev.ts` | ソース | リビジョン番号バリデーション |
| PadType.ts | `src/node/types/PadType.ts` | 型定義 | パッド関連の型定義 |
| pad.ts | `src/tests/backend/specs/api/pad.ts` | テスト | APIテストケース |
