# 機能設計書 16-テキスト設定

## 概要

本ドキュメントは、Etherpadにおけるテキスト設定機能（setText）の設計仕様を定義する。この機能は、指定されたパッドのテキストコンテンツを新しいテキストで置き換えるためのAPIを提供する。

### 本機能の処理概要

テキスト設定機能は、パッドの既存テキストを完全に新しいテキストで置き換える機能である。置き換え後、接続中のすべてのクライアントに変更が同期される。

**業務上の目的・背景**：
外部システムからパッドの内容を一括更新したい場合や、テンプレートテキストでパッドを初期化したい場合に使用される。また、自動化されたワークフローでパッドの内容を更新する際にも活用される。ドキュメント生成システムやCMSとの連携において、生成されたコンテンツをEtherpadに反映させるためにも使用される。

**機能の利用シーン**：
- 外部システムからのコンテンツインポート
- テンプレートによるパッド初期化
- 自動生成コンテンツの反映
- バッチ処理による一括更新
- ドキュメント変換後の内容設定

**主要な処理内容**：
1. 対象パッドの存在確認
2. テキストパラメータの検証
3. パッドのテキストを新しいテキストで置き換え
4. リビジョンとして記録
5. 接続中クライアントへの変更通知

**関連システム・外部連携**：
- REST API（POST /api/2/pads/text）を通じて外部システムから呼び出し可能
- Socket.IO経由で接続中クライアントにリアルタイム反映

**権限による制御**：
- APIキーまたはOAuth2トークンによる認証が必要
- パッドの内容を上書きする権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能はAPIのみで提供され、直接関連する画面はない |

## 機能種別

データ更新（Update操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | テキストを設定するパッドのID | 正規表現に一致、存在するパッドであること |
| text | string | Yes | 設定する新しいテキスト | 文字列型であること |
| authorId | string | No | 変更を行う著者のID（デフォルト: 空文字列） | 任意の文字列 |

### 入力データソース

REST API経由でのHTTPリクエスト（POST /api/2/pads/text）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | number | 結果コード（0: 成功、1: エラー、4: 認証エラー） |
| message | string | 結果メッセージ（"ok" または エラーメッセージ） |
| data | null | 成功時はnull |

### 出力先

- HTTPレスポンス（JSON形式）
- Socket.IO経由で接続中クライアントに変更通知

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ POST /api/2/pads/text
2. 認証チェック
   └─ APIキーまたはOAuth2トークンの検証
3. パラメータ抽出
   └─ padID, text, authorIdの取得
4. テキスト検証
   └─ 文字列型であることを確認
5. パッド取得
   └─ getPadSafe(padID, true)
6. テキスト設定
   └─ pad.setText(text, authorId)
   └─ 内部でspliceText→appendRevision
7. クライアント通知
   └─ padMessageHandler.updatePadClients(pad)
8. レスポンス返却
   └─ {code: 0, message: "ok", data: null}
```

### フローチャート

```mermaid
flowchart TD
    A[開始: setText API呼び出し] --> B{認証チェック}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D{textは文字列?}
    D -->|No| E[400 text is not a string]
    D -->|Yes| F[パッド存在確認]
    F -->|存在しない| G[400 padID does not exist]
    F -->|存在する| H[pad.setText実行]
    H --> I[appendRevision]
    I --> J[DB保存]
    J --> K[updatePadClients]
    K --> L[接続クライアントに通知]
    L --> M[200 OK]
    M --> N[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-016-01 | 完全置換 | 既存テキストは完全に新しいテキストで置き換えられる | 常時 |
| BR-016-02 | 改行付加 | テキスト末尾に改行がない場合は自動で付加 | テキスト末尾が改行でない場合 |
| BR-016-03 | リビジョン記録 | 変更は新しいリビジョンとして記録される | 常時 |
| BR-016-04 | 著者記録 | authorId指定時は著者として記録 | authorId指定時 |
| BR-016-05 | リアルタイム同期 | 変更は接続中クライアントに即座に通知 | 常時 |

### 計算ロジック

テキスト置換のChangeset生成:
1. 既存テキストの長さを取得
2. makeSplice(原文, 0, 原文長さ, 新テキスト)でChangeset生成
3. appendRevisionでリビジョン追加

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 参照 | pad:{padID} | SELECT | パッドメタデータ取得 |
| 更新 | pad:{padID} | UPDATE | atext, head, pool更新 |
| 挿入 | pad:{padID}:revs:{n} | INSERT | 新リビジョン追加 |
| 更新 | globalAuthor:{authorId} | UPDATE | 著者のパッド関連付け |

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

#### pad:{padID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | atext | 新しいテキストとその属性 | setText後の状態 |
| UPDATE | head | +1（新リビジョン番号） | リビジョン番号インクリメント |
| UPDATE | pool | 著者属性追加 | authorId指定時 |

#### pad:{padID}:revs:{n}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | changeset | テキスト置換のChangeset | 新リビジョン |
| INSERT | meta.author | authorId | 変更者 |
| INSERT | meta.timestamp | 現在時刻 | 変更日時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | apierror | パッドが存在しない | 正しいパッドIDを指定 |
| 1 | apierror | textが文字列でない | 文字列としてtextを送信 |
| 4 | authError | 認証情報が無効 | 正しいAPIキーまたはトークンを使用 |

### リトライ仕様

データベース操作に関してはueberDB2のリトライ機構に依存。

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

pad更新とrevs挿入はPromise.allで並列実行される。どちらかが失敗した場合、データ不整合が発生する可能性があるが、次回のsetTextで修復可能。

## パフォーマンス要件

- テキストサイズに比例した処理時間
- 大量のクライアントが接続している場合、通知に時間がかかる可能性
- Changeset生成は差分計算のため、長文では負荷が高くなる

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

- APIキーまたはOAuth2認証が必須
- パッドの内容を完全に上書きするため、破壊的操作として扱う
- 入力テキストは特殊文字（制御文字、タブ等）がサニタイズされる

## 備考

- Windows改行（\r\n）は自動的にUnix改行（\n）に変換される
- タブ文字は8つのスペースに変換される
- ノンブレークスペース（\xa0）は通常のスペースに変換される
- 既存の書式情報（太字、リスト等）はすべて失われる

---

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

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

### 推奨読解順序

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

パッドのテキスト更新における内部構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Pad.ts | `src/node/db/Pad.ts` | setText関数（309-311行目）、spliceText関数（284-299行目） |
| 1-2 | Pad.ts | `src/node/db/Pad.ts` | cleanText関数（36-39行目） |

**読解のコツ**: setTextはspliceTextへの委譲、spliceTextはmakeSpliceでChangeset生成後appendRevisionを呼び出す構造。

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

REST APIからの呼び出しフローを追跡する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | API定義（1391-1417行目でsetTextのマッピング） |
| 2-2 | APIHandler.ts | `src/node/handler/APIHandler.ts` | パラメータ定義（142行目でsetTextの引数） |

**主要処理フロー**:
1. **1391-1417行目**: POST /pads/text のルート定義
2. **142行目**: setTextの引数（padID, text, authorId）

#### Step 3: ビジネスロジックを理解する

実際のテキスト設定ロジックを読み解く。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | setText関数（205-216行目） |
| 3-2 | Pad.ts | `src/node/db/Pad.ts` | setText関数（309-311行目） |
| 3-3 | Pad.ts | `src/node/db/Pad.ts` | spliceText関数（284-299行目） |
| 3-4 | Pad.ts | `src/node/db/Pad.ts` | appendRevision関数（97-144行目） |
| 3-5 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | updatePadClients関数 |

**主要処理フロー**:
- **API.ts 207-209行目**: テキスト型チェック
- **API.ts 212行目**: パッド取得
- **API.ts 214行目**: pad.setText呼び出し
- **API.ts 215行目**: updatePadClients呼び出し
- **Pad.ts 310行目**: spliceText(0, 全長, 新テキスト)

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

```
POST /api/2/pads/text (RestAPI.ts)
    │
    └─ apiHandler.handle() (APIHandler.ts)
           │
           └─ api.setText() (API.ts:205-216)
                  │
                  ├─ テキスト型チェック (207-209行目)
                  │
                  ├─ getPadSafe(padID, true) (212行目)
                  │
                  ├─ pad.setText(text, authorId) (214行目)
                  │      │
                  │      └─ pad.spliceText(0, 全長, text, authorId) (Pad.ts:310行目)
                  │             │
                  │             ├─ cleanText(ins) (290行目)
                  │             ├─ makeSplice() (297行目)
                  │             └─ appendRevision(changeset, authorId) (298行目)
                  │                    │
                  │                    ├─ applyToAText() (98行目)
                  │                    ├─ db.set(revs:) (113-123行目)
                  │                    ├─ saveToDatabase() (124行目)
                  │                    └─ hooks.aCallAll('padUpdate') (126行目)
                  │
                  └─ padMessageHandler.updatePadClients(pad) (215行目)
```

### データフロー図

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

padID         ───────▶ getPadSafe() ──────────────▶ Padオブジェクト
text                         │
authorId                     ▼
                    pad.setText()
                         │
                         ├─▶ cleanText(text)
                         │      └─▶ 改行・タブ正規化
                         │
                         ├─▶ spliceText()
                         │      └─▶ makeSplice() ─▶ Changeset
                         │
                         └─▶ appendRevision()
                                │
                                ├─▶ applyToAText() ─▶ 新atext
                                │
                                ├─▶ pad:{padID}:revs:{n} [INSERT]
                                │
                                ├─▶ pad:{padID} [UPDATE]
                                │
                                └─▶ globalAuthor:{id} [UPDATE]
                                │
                                ▼
                    updatePadClients()
                         │
                         └─▶ Socket.IO通知 ───────▶ 接続クライアント
                                │
                                ▼
                    {code: 0, message: "ok"} ─▶ JSON Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | setText関数の実装 |
| Pad.ts | `src/node/db/Pad.ts` | ソース | Padクラス、setText/spliceText/appendRevision |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | REST APIエンドポイント定義 |
| APIHandler.ts | `src/node/handler/APIHandler.ts` | ソース | API呼び出しハンドリング |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | クライアント通知 |
| Changeset.ts | `src/static/js/Changeset.ts` | ソース | makeSplice関数 |
| DB.ts | `src/node/db/DB.ts` | ソース | データベースアクセス層 |
