# 機能設計書 7-グループパッド作成

## 概要

本ドキュメントは、Etherpad APIにおける「グループパッド作成」機能の設計仕様を記載する。

### 本機能の処理概要

指定されたグループ内に新しいパッドを作成する機能である。グループパッドはアクセス制御が可能で、セッションベースの認証により特定のユーザーのみがアクセスできるようになる。

**業務上の目的・背景**：
企業やプロジェクトチームでは、パッドへのアクセスを制限したい場合がある。グループパッド機能により、グループに紐づいたパッドを作成し、セッション認証を通じてアクセス制御が可能になる。これにより、機密性の高いドキュメントの共同編集を安全に行える。

**機能の利用シーン**：
- プロジェクトチーム専用のパッドを作成する場合
- 認証済みユーザーのみがアクセスできるパッドを作成する場合
- LMS等の外部システムからコースに紐づくパッドを作成する場合
- プライベートなドキュメント共有を行う場合

**主要な処理内容**：
1. グループの存在確認
2. パッドIDの生成（`{groupID}${padName}`形式）
3. パッドの重複確認
4. パッドの作成と初期テキストの設定
5. グループレコードへのパッド登録

**関連システム・外部連携**：
- ueberDB2データベースによるデータ永続化
- セッション管理機能との連携

**権限による制御**：
APIキーまたはOAuth2トークンによる認証が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面からの直接操作はなく、API経由でのみ利用される |

## 機能種別

CRUD操作（Create）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| groupID | string | Yes | パッドを作成するグループのID | 存在するグループIDであること |
| padName | string | Yes | パッドの名前（ID生成に使用） | 有効なパッド名であること |
| text | string | No | パッドの初期テキスト | 100,000文字以下 |
| authorId | string | No | 作成者の著者ID | 空文字列可 |
| apikey | string | Yes（APIキー認証時） | API認証キー | 設定ファイルのAPIキーと一致すること |

### 入力データソース

HTTP POSTリクエスト（`/api/2/pads/group`）のボディから取得。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | number | 処理結果コード（0: 成功） |
| message | string | 処理結果メッセージ（"ok"） |
| data.padID | string | 作成されたパッドID |

### 出力先

HTTPレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. API認証
   └─ APIキーまたはOAuth2トークンを検証
2. グループ存在確認
   └─ doesGroupExist(groupID)を呼び出し
   └─ 存在しなければエラー返却
3. パッドID生成
   └─ padID = `${groupID}$${padName}`
4. パッド重複確認
   └─ doesPadExists(padID)を呼び出し
   └─ 存在する場合はエラー返却
5. パッド作成
   └─ padManager.getPad(padID, text, authorId)を呼び出し
6. グループへのパッド登録
   └─ db.setSub(`group:${groupID}`, ['pads', padID], 1)
7. レスポンス返却
   └─ {padID: "g.xxx$padName"} を返却
```

### フローチャート

```mermaid
flowchart TD
    A[POST /api/2/pads/group] --> B{認証検証}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D[doesGroupExist groupID]
    D --> E{グループ存在?}
    E -->|No| F[400 groupID does not exist]
    E -->|Yes| G["padID = groupID + $ + padName"]
    G --> H[doesPadExists padID]
    H --> I{パッド存在?}
    I -->|Yes| J[400 padName does already exist]
    I -->|No| K["padManager.getPad(padID, text, authorId)"]
    K --> L["db.setSub group:groupID, pads/padID, 1"]
    L --> M["返却 {padID}"]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | グループ必須 | グループが存在しないとパッド作成不可 | 常に |
| BR-002 | 重複禁止 | 同じグループ内で同じpadNameは使用不可 | 常に |
| BR-003 | パッドID形式 | パッドIDは`{groupID}${padName}`形式 | 常に |
| BR-004 | グループ登録 | 作成後、グループのpadsに登録される | 常に |

### 計算ロジック

- パッドID生成: `` `${groupID}$${padName}` ``
- 例: `g.s8oes9dhwrvt0zif$meeting-notes`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| グループ存在確認 | group:{groupID} | SELECT | グループ実体確認 |
| パッド存在確認 | pad:{padID} | SELECT | 重複チェック |
| パッド作成 | pad:{padID} | INSERT | パッド本体作成 |
| リビジョン作成 | pad:{padID}:revs:0 | INSERT | 初期リビジョン作成 |
| グループ更新 | group:{groupID} | UPDATE | padsにパッドIDを追加 |
| 著者登録 | author:{authorId} | UPDATE | 著者のパッドリストに追加 |

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

#### group:{groupID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | 存在確認 | doesGroupExist経由 |
| UPDATE | pads.{padID} | 1 | パッド登録 |

#### pad:{padID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | 全項目 | 初期値 | Pad.init経由 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 1 | パラメータエラー | groupIDが存在しない | 正しいgroupIDを指定 |
| 1 | パラメータエラー | padNameが既に存在する | 別のpadNameを指定 |
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを指定 |
| 2 | 内部エラー | データベース操作失敗 | サーバーログを確認 |

### リトライ仕様

データベース操作失敗時のリトライはueberDB2層で処理される。

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

パッド作成とグループ登録は順次実行される。途中で失敗した場合、パッドは作成されているがグループに登録されていない状態になる可能性がある。

## パフォーマンス要件

- レスポンス時間: 通常200ms以下
- 初期テキストのサイズに応じて処理時間が増加

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

- APIキーまたはOAuth2トークンによる認証が必須
- グループパッドへのアクセスにはセッションが必要
- publicStatusをtrueに設定しない限り、認証なしではアクセス不可

## 備考

- APIバージョン1.3.0からauthorIdパラメータが追加
- グループパッドのURLは`/p/{groupID}${padName}`形式

---

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

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

### 推奨読解順序

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

グループパッドのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | GroupManager.ts | `src/node/db/GroupManager.ts` | createGroupPad関数とgroup.padsの構造 |
| 1-2 | Pad.ts | `src/node/db/Pad.ts` | Padクラスの構造 |

**読解のコツ**: グループパッドのIDは`{groupID}${padName}`形式で、`$`がグループIDとパッド名の区切りになる。グループの`pads`プロパティにパッドIDが登録される。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | POST /pads/group のルーティング |
| 2-2 | APIHandler.ts | `src/node/handler/APIHandler.ts` | createGroupPadパラメータ定義 |

**主要処理フロー**:
1. **1276-1306行目（RestAPI.ts）**: POST /pads/group のルーティング定義
2. **41行目（APIHandler.ts）**: `createGroupPad: ['groupID', 'padName', 'text']`
3. **138行目（APIHandler.ts）**: バージョン1.3.0で`createGroupPad: ['groupID', 'padName', 'text', 'authorId']`

#### Step 3: コアロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | createGroupPad関数のエクスポート |
| 3-2 | GroupManager.ts | `src/node/db/GroupManager.ts` | createGroupPad関数の実装 |

**主要処理フロー**:
- **49行目（API.ts）**: `exports.createGroupPad = groupManager.createGroupPad;`
- **137-163行目（GroupManager.ts）**: createGroupPad関数の実装
  - **139行目**: パッドID生成 `` `${groupID}$${padName}` ``
  - **142行目**: `doesGroupExist(groupID)` でグループ存在確認
  - **144-146行目**: グループ不在時のエラー
  - **149行目**: `doesPadExists(padID)` でパッド重複確認
  - **151-153行目**: パッド重複時のエラー
  - **157行目**: `padManager.getPad(padID, text, authorId)` でパッド作成
  - **160行目**: `db.setSub` でグループにパッド登録

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

```
POST /api/2/pads/group (RestAPI.ts)
    │
    ├─ APIHandler.handle() (APIHandler.ts:161)
    │      │
    │      ├─ 認証検証 (APIHandler.ts:175-200)
    │      │
    │      └─ api.createGroupPad(groupID, padName, text, authorId) (API.ts:49)
    │              │
    │              └─ groupManager.createGroupPad() (GroupManager.ts:137)
    │                      │
    │                      ├─ パッドID生成: `${groupID}$${padName}`
    │                      │
    │                      ├─ doesGroupExist(groupID) (GroupManager.ts:85)
    │                      │
    │                      ├─ padManager.doesPadExists(padID)
    │                      │
    │                      ├─ padManager.getPad(padID, text, authorId)
    │                      │      └─ Pad.init()
    │                      │
    │                      └─ db.setSub(`group:${groupID}`, ['pads', padID], 1)
    │
    └─ Response: {code: 0, message: "ok", data: {padID}}
```

### データフロー図

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

HTTP POST            ─────▶ RestAPI.ts
/api/2/pads/group           ルーティング
                                │
groupID              ─────▶ GroupManager.ts
padName                     │
text                        ├─ padID = groupID + $ + padName
authorId                    │
                            ├─ doesGroupExist()
                            │
                            ├─ doesPadExists()
                            │
                            ├─ padManager.getPad()
                            │      └─ Pad.init()
                            │
                            └─ db.setSub(group, pads)
                                    │
                                    ▼
                            {padID} ─────▶ JSON Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | APIルーティング定義 |
| APIHandler.ts | `src/node/handler/APIHandler.ts` | ソース | API認証・パラメータ処理 |
| API.ts | `src/node/db/API.ts` | ソース | API関数エクスポート |
| GroupManager.ts | `src/node/db/GroupManager.ts` | ソース | createGroupPad実装 |
| PadManager.ts | `src/node/db/PadManager.ts` | ソース | パッド管理・キャッシュ |
| Pad.ts | `src/node/db/Pad.ts` | ソース | Padクラス実装 |
| DB.ts | `src/node/db/DB.ts` | ソース | データベースアクセス層 |
