# 機能設計書 33-差分HTML作成

## 概要

本ドキュメントは、Etherpadの差分HTML作成機能（createDiffHTML）の設計仕様を記載する。

### 本機能の処理概要

本機能は、指定されたパッドの2つのリビジョン間の差分をHTML形式で生成するAPI機能である。追加・削除・変更された内容が視覚的に識別可能な形式で出力される。

**業務上の目的・背景**：共同編集において、どの部分がいつ誰によって変更されたかを把握することは重要である。この機能は、2つの時点間での変更内容を視覚的に比較できるHTML形式で提供することで、レビューや履歴確認を効率化する。特にタイムスライダー機能での差分表示や、変更履歴のレポート生成に活用される。

**機能の利用シーン**：タイムスライダーで2つのリビジョン間の変更を確認する場合、変更履歴レポートを生成する場合、レビュープロセスで変更内容を確認する場合など。

**主要な処理内容**：
1. パッドIDと開始・終了リビジョン番号の検証
2. PadDiffオブジェクトの生成
3. 2つのリビジョン間の変更セットを統合
4. 差分情報を含むHTMLを生成
5. 変更に貢献した著者一覧を取得

**関連システム・外部連携**：REST API経由でHTTPリクエストを受け付け、JSON形式でレスポンスを返す。ExportHtmlモジュールを使用してHTML変換を行う。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 3 | タイムスライダー画面 | 主画面 | リビジョン間の差分表示 |

## 機能種別

データ取得・変換処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | 対象パッドのID | 文字列型であること、パッドが存在すること |
| startRev | integer | Yes | 比較開始リビジョン番号 | 整数型であること、0以上であること |
| endRev | integer | Yes | 比較終了リビジョン番号 | 整数型であること、startRev以上であること |

### 入力データソース

REST API POSTリクエストのボディとしてJSON形式でパラメータを受け取る。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | integer | 結果コード（0: 成功） |
| message | string | 結果メッセージ（"ok"） |
| data.html | string | 差分を含むHTML文字列 |
| data.authors | array | 変更に貢献した著者IDの配列 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ POST /api/2/pads/diff
2. 認証・認可チェック
   └─ APIキーまたはSSOトークンの検証
3. パラメータ検証
   └─ startRev, endRevの数値検証
4. パッド存在確認・取得
   └─ padManager.getPadを呼び出し
5. リビジョン範囲調整
   └─ headRevを超える場合は調整
6. PadDiffオブジェクト生成
   └─ new PadDiff(pad, startRev, endRev)
7. 差分HTML生成
   └─ padDiff.getHtml()を呼び出し
8. 著者一覧取得
   └─ padDiff.getAuthors()を呼び出し
9. レスポンス生成・返却
   └─ {html, authors}形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{認証チェック}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D{パラメータ検証}
    D -->|不正| E[400 Bad Request]
    D -->|正常| F{パッド存在確認}
    F -->|存在しない| G[padID does not exist]
    F -->|存在する| H[リビジョン範囲調整]
    H --> I[PadDiffオブジェクト生成]
    I --> J{生成成功?}
    J -->|失敗| K[Invalid revision range]
    J -->|成功| L[差分HTML生成]
    L --> M[著者一覧取得]
    M --> N[JSONレスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-33-01 | パッド存在必須 | 指定されたパッドIDが存在しない場合はエラー | 常時 |
| BR-33-02 | リビジョン範囲調整 | startRev/endRevがheadを超える場合は自動的にheadに調整 | head超過時 |
| BR-33-03 | 著者色情報付与 | 差分HTMLには著者ごとの色情報が付与される | 常時 |
| BR-33-04 | 削除マーカー | 削除された内容は「removed」属性でマーキング | 削除時 |

### 計算ロジック

差分HTMLの生成は以下のロジックで行われる：
1. 開始リビジョンのatextをクリア（著者情報を空に）
2. 各リビジョンの変更セットを順次適用
3. 削除操作は「removed」属性でマーキング
4. 著者情報を保持しながら変更セットを合成
5. 最終的なatextをHTML形式に変換

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| パッド情報取得 | pad:{padID} | SELECT | パッドのメタ情報を取得 |
| リビジョン取得 | pad:{padID}:revs:{rev} | SELECT | 各リビジョンの変更セットを取得 |

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

#### pad:{padID}:revs:{rev}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | changeset, meta | startRevからendRevまでの各リビジョン | 差分計算に使用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | apierror | パッドが存在しない | 存在するパッドIDを指定 |
| 1 | stop | 無効なリビジョン範囲 | 有効なstartRev, endRevを指定 |
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを使用 |

### リトライ仕様

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

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

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

## パフォーマンス要件

- レスポンス時間: リビジョン範囲により変動
  - 100リビジョン以内: 1秒以内
  - 1000リビジョン以上: 数秒かかる場合あり
- 一括取得（bulkSize: 100）により効率化

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

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

## 備考

API Version 1.2.7で追加された機能。

---

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

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

### 推奨読解順序

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

まず、PadDiffクラスの構造と差分計算の仕組みを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | padDiff.ts | `src/node/utils/padDiff.ts` | PadDiffクラスの定義と主要メソッド |

**読解のコツ**: PadDiffクラスは_createDiffAtext()で差分atextを生成し、getHtml()でHTML変換を行う。

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

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

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

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

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

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

**主要処理フロー**:
- **830-839行目**: パラメータの検証
- **842-846行目**: パッド取得とリビジョン範囲調整
- **848-853行目**: PadDiffオブジェクトの生成
- **855-858行目**: HTML生成と著者一覧取得

#### Step 4: PadDiffクラスの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | padDiff.ts | `src/node/utils/padDiff.ts` | _createDiffAtext()メソッド（133-186行目） |
| 4-2 | padDiff.ts | `src/node/utils/padDiff.ts` | getHtml()メソッド（187-203行目） |
| 4-3 | padDiff.ts | `src/node/utils/padDiff.ts` | _extendChangesetWithAuthor()メソッド（215-241行目） |

**主要処理フロー**:
- **133-186行目**: 差分atextの生成ロジック
- **187-203行目**: HTMLへの変換処理
- **215-241行目**: 削除操作への著者情報付与

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

```
RestAPI.expressCreateServer
    │
    ├─ app.use('/api/2', ...)
    │      └─ apiHandler.handle('1.2.7', 'createDiffHTML', fields)
    │              └─ API.createDiffHTML(padID, startRev, endRev)
    │                      ├─ checkValidRev(startRev)
    │                      ├─ checkValidRev(endRev)
    │                      ├─ getPadSafe(padID, true)
    │                      ├─ new PadDiff(pad, startRev, endRev)
    │                      │       ├─ _createDiffAtext()
    │                      │       │       ├─ _createClearStartAtext()
    │                      │       │       ├─ _getChangesetsInBulk()
    │                      │       │       ├─ _extendChangesetWithAuthor()
    │                      │       │       └─ _createDeletionChangeset()
    │                      │       └─ getHtml()
    │                      │               └─ exportHtml.getHTMLFromAtext()
    │                      └─ padDiff.getAuthors()
    │
    └─ res.json({html, authors})
```

### データフロー図

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

padID ─────────────────────▶ getPadSafe() ───────────────▶ Padオブジェクト
                                     │
startRev, endRev ──────────▶ PadDiff()
                                     │
                                     ▼
                          _createClearStartAtext()
                                     │
                                     ▼
Revisions ────────────────▶ _getChangesetsInBulk()
                                     │
                                     ▼
                          _extendChangesetWithAuthor()
                                     │
                                     ▼
                          _createDeletionChangeset()
                                     │
                                     ▼
                          exportHtml.getHTMLFromAtext() ──▶ html
                                     │
                                     ▼
                          _authors ────────────────────────▶ authors[]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | createDiffHTML関数の実装 |
| padDiff.ts | `src/node/utils/padDiff.ts` | ソース | PadDiffクラスの実装 |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | REST APIエンドポイント定義 |
| ExportHtml.ts | `src/node/utils/ExportHtml.ts` | ソース | HTML変換処理 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | Padクラスの定義 |
| Changeset.ts | `src/static/js/Changeset.ts` | ソース | Changeset操作関数群 |
