# 機能設計書 39-著者条件付き作成

## 概要

本ドキュメントは、Etherpadの著者条件付き作成機能（createAuthorIfNotExistsFor）の設計仕様を記載する。

### 本機能の処理概要

本機能は、指定されたマッパー（外部識別子）に対応する著者が存在しない場合のみ新規作成し、存在する場合は既存の著者IDを返すAPI機能である。これにより、外部システムとの統合において一貫した著者管理が可能となる。

**業務上の目的・背景**：外部認証システムやSSO環境と連携する場合、ユーザーを一意に識別するための外部ID（マッパー）が使用される。この機能は、外部IDと著者IDを紐づけることで、同一ユーザーが複数回アクセスしても一貫した著者IDを使用できるようにする。これにより、編集履歴が正しくユーザーに帰属される。

**機能の利用シーン**：SSO認証時のユーザーセッション作成、外部システムとの連携時、ユーザー初回アクセス時の著者自動作成など。

**主要な処理内容**：
1. マッパー（外部ID）の検証
2. マッパーに対応する著者の検索（mapper2author:{mapper}）
3. 存在しない場合は新規著者を作成してマッピングを保存
4. 存在する場合はタイムスタンプを更新
5. 名前が指定されている場合は著者名を設定
6. 著者IDを返却

**関連システム・外部連携**：REST API経由でHTTPリクエストを受け付け、JSON形式でレスポンスを返す。データベース（ueberDB2）に著者データとマッピングを保存する。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | API経由 | 主機能 | REST API呼び出しで著者を条件付き作成 |

## 機能種別

データ追加/更新（UPSERT操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| authorMapper | string | Yes | 外部識別子（マッパー） | 文字列型であること |
| name | string | No | 著者の表示名 | - |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | integer | 結果コード（0: 成功） |
| message | string | 結果メッセージ（"ok"） |
| data.authorID | string | 著者ID（既存または新規作成） |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ POST /api/2/authors/createIfNotExistsFor
2. 認証・認可チェック
   └─ APIキーまたはSSOトークンの検証
3. マッパー検索
   └─ db.get(`mapper2author:${authorMapper}`)
4. 著者存在確認
   └─ 存在しない場合：
       ├─ createAuthor()で新規作成
       └─ db.set(`mapper2author:${mapper}`, authorID)
   └─ 存在する場合：
       └─ db.setSub(`globalAuthor:${author}`, ['timestamp'], Date.now())
5. 名前設定（指定時）
   └─ setAuthorName(authorID, name)
6. レスポンス生成・返却
   └─ {authorID: string}形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{認証チェック}
    B -->|失敗| C[401 Unauthorized]
    B -->|成功| D[マッパー検索]
    D --> E{著者存在?}
    E -->|存在しない| F[createAuthor]
    F --> G[マッピング保存]
    E -->|存在する| H[タイムスタンプ更新]
    G --> I{name指定?}
    H --> I
    I -->|指定あり| J[著者名設定]
    I -->|指定なし| K[JSONレスポンス返却]
    J --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | マッパー必須 | authorMapperパラメータは必須 | 常時 |
| BR-39-02 | 冪等性 | 同じマッパーで複数回呼び出しても同じ著者IDを返す | 常時 |
| BR-39-03 | タイムスタンプ更新 | 既存著者へのアクセス時はタイムスタンプが更新される | 著者存在時 |
| BR-39-04 | 名前上書き | nameが指定された場合は既存の名前を上書きする | name指定時 |

### 計算ロジック

マッピング検索:
```javascript
const author = await db.get(`mapper2author:${mapper}`);
```

新規作成時のマッピング保存:
```javascript
await db.set(`mapper2author:${mapper}`, author.authorID);
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| マッピング検索 | mapper2author:{mapper} | SELECT | マッパーから著者IDを検索 |
| マッピング保存 | mapper2author:{mapper} | INSERT | 新規マッピングを保存 |
| 著者作成 | globalAuthor:{authorID} | INSERT | 新規著者を作成 |
| タイムスタンプ更新 | globalAuthor:{authorID} | UPDATE | タイムスタンプを更新 |
| 著者名更新 | globalAuthor:{authorID} | UPDATE | 著者名を設定 |

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

#### mapper2author:{mapper}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | (全体) | キー: mapper2author:{mapper} | 著者IDを格納 |
| INSERT | (全体) | 著者ID文字列 | 新規マッピング時 |

#### globalAuthor:{authorID}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | timestamp | Date.now() | 既存著者アクセス時 |
| UPDATE | name | 入力値 | 名前指定時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 4 | 認証エラー | APIキーが無効 | 正しいAPIキーを使用 |

### リトライ仕様

リトライは安全（冪等な操作）

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

複数のデータベース操作があるが、競合状態は許容される（最終的に一貫性が保たれる）。

## パフォーマンス要件

- レスポンス時間: 200ms以内（通常条件下）
- マッピング検索と著者作成で2-3回のDBアクセス

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

- APIキー認証またはSSO認証が必須
- マッパーは外部システムから提供される識別子を使用

## 備考

API Version 1.0で追加された機能。セッション作成時に著者の自動作成に使用される。

---

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

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

### 推奨読解順序

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

まず、マッピングと著者オブジェクトの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | AuthorManager.ts | `src/node/db/AuthorManager.ts` | mapAuthorWithDBKey関数（117-138行目） |
| 1-2 | AuthorManager.ts | `src/node/db/AuthorManager.ts` | createAuthorIfNotExistsFor関数（182-191行目） |

**読解のコツ**: mapper2authorキーでマッパーと著者IDの紐づけを管理。既存著者の場合はタイムスタンプのみ更新。

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

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

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

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | createAuthorIfNotExistsForのエクスポート（62行目） |
| 3-2 | AuthorManager.ts | `src/node/db/AuthorManager.ts` | 関数実装（182-191行目） |

**主要処理フロー**:
- **183行目**: mapAuthorWithDBKey('mapper2author', authorMapper)呼び出し
- **185-188行目**: 名前が指定されている場合は設定
- **190行目**: 著者情報を返却

#### Step 4: マッピング処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | AuthorManager.ts | `src/node/db/AuthorManager.ts` | mapAuthorWithDBKey関数（117-138行目） |

**主要処理フロー**:
- **119行目**: db.get(`${mapperkey}:${mapper}`)でマッピング検索
- **121-129行目**: 存在しない場合は新規作成してマッピング保存
- **134行目**: 存在する場合はタイムスタンプ更新

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

```
RestAPI.expressCreateServer
    │
    ├─ app.use('/api/2', ...)
    │      └─ apiHandler.handle('1', 'createAuthorIfNotExistsFor', fields)
    │              └─ API.createAuthorIfNotExistsFor(authorMapper, name)
    │                      └─ authorManager.createAuthorIfNotExistsFor(authorMapper, name)
    │                              ├─ mapAuthorWithDBKey('mapper2author', authorMapper)
    │                              │       ├─ db.get(`mapper2author:${mapper}`)
    │                              │       ├─ [新規] createAuthor() + db.set(mapping)
    │                              │       └─ [既存] db.setSub(timestamp)
    │                              ├─ [name指定時] setAuthorName(authorID, name)
    │                              └─ return {authorID}
    │
    └─ res.json({authorID})
```

### データフロー図

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

authorMapper ─────────────▶ db.get(`mapper2author:${mapper}`)
                                     │
                         ┌───────────┴───────────┐
                         ▼                       ▼
                   [著者なし]               [著者あり]
                         │                       │
                         ▼                       ▼
               createAuthor()           db.setSub(timestamp)
                         │                       │
                         ▼                       │
               db.set(mapping)                   │
                         │                       │
                         └───────────┬───────────┘
                                     ▼
name ─────────────────────▶ setAuthorName() (指定時)
                                     │
                                     ▼
                           {authorID} ─────────▶ JSONレスポンス
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | createAuthorIfNotExistsForのエクスポート |
| AuthorManager.ts | `src/node/db/AuthorManager.ts` | ソース | 著者管理の実装 |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | REST APIエンドポイント定義 |
