# 機能設計書 43-テストバックエンド

## 概要

本ドキュメントは、Decap CMSのテストバックエンド（`decap-cms-backend-test`）の機能設計を記述する。ブラウザのメモリ（`window.repoFiles`/`window.repoFilesUnpublished`）をストレージとして使用する開発・テスト・デモ用のモックバックエンドである。

### 本機能の処理概要

**業務上の目的・背景**：Decap CMSの開発・テスト・デモンストレーションにおいて、外部Gitサービスやプロキシサーバーへの接続なしにCMSの全機能を動作確認できる環境が必要である。テストバックエンドは、windowオブジェクト上のインメモリデータストアでファイルシステムをシミュレートし、全てのバックエンド操作を完結させる。

**機能の利用シーン**：CMSの公式デモサイト、開発時のE2Eテスト、UIコンポーネントのテスト環境で使用される。config.ymlで`backend.name: test-repo`を指定することで有効化される。`backend.login: false`を設定するとログイン画面をスキップできる。

**主要な処理内容**：
1. `window.repoFiles`ツリー構造でのファイルシステムシミュレーション
2. `window.repoFilesUnpublished`でのエディトリアルワークフローシミュレーション
3. カーソルベースのページネーション（1ページ10件）
4. エントリのCRUD操作（読み込み・作成・更新・削除）
5. メディアファイルの管理（アップロード・削除・URL生成）
6. 未公開エントリのステータス管理と公開処理

**関連システム・外部連携**：外部システムとの連携は一切ない。全てインメモリで完結する。

**権限による制御**：認証はバイパスされる。`backend.login: false`設定時はログイン画面もスキップされる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 全画面共通 | バックエンド | 全てのエントリ/メディア操作のインメモリバックエンド |

## 機能種別

CRUD操作（インメモリ）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config.media_folder | string | Yes | メディアフォルダパス | 設定ファイルより取得 |
| config.backend.login | boolean | No | false時ログインスキップ | デフォルト: true |
| options.initialWorkflowStatus | string | No | 初期ワークフローステータス | - |

### 入力データソース

- `window.repoFiles`: インメモリファイルツリー
- `window.repoFilesUnpublished`: 未公開エントリストア

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| entries | ImplementationEntry[] | エントリデータ配列（data, file: {path, id}） |
| mediaFiles | object[] | メディアファイル配列（id, name, size, path, url, displayURL, fileObj） |
| cursor | Cursor | ページネーションカーソル |

### 出力先

- CMS状態管理（Redux Store）

## 処理フロー

### 処理シーケンス

```
1. 初期化
   └─ window.repoFiles/repoFilesUnpublishedの存在確認・初期化
2. エントリ読み込み
   └─ getFolderFiles()でツリーを再帰走査
   └─ 拡張子フィルタ・深度制限
   └─ ページネーションカーソル生成（pageSize=10）
3. エントリ保存
   └─ ワークフロー使用時: repoFilesUnpublishedに保存
   └─ 通常時: repoFilesにwriteFile
4. エントリ公開
   └─ repoFilesUnpublishedからrepoFilesにデータ移行
   └─ ファイルのリネーム対応（originalPath → 新パス）
5. メディア管理
   └─ normalizeAsset()でFileObjからObjectURLを生成
   └─ UUID v4でIDを付与
```

### フローチャート

```mermaid
flowchart TD
    A[CMS操作] --> B{操作種別}
    B -->|読み込み| C[getFolderFiles/getFile]
    B -->|保存| D{useWorkflow?}
    B -->|メディア| E[normalizeAsset]
    C --> F[window.repoFiles走査]
    D -->|Yes| G[repoFilesUnpublished更新]
    D -->|No| H[repoFiles writeFile]
    E --> I[ObjectURL生成]
    F --> J[Cursor付きレスポンス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | ページサイズ | 1ページあたり10件（pageSize=10） | entriesByFolder |
| BR-43-02 | ログインスキップ | login===falseの場合componentDidMountでonLogin自動実行 | AuthenticationPage |
| BR-43-03 | ワークフロー分岐 | useWorkflow時はrepoFilesUnpublishedに保存 | persistEntry |
| BR-43-04 | 公開処理 | 未公開エントリの全diffをrepoFilesに反映しキーを削除 | publishUnpublishedEntry |
| BR-43-05 | ObjectURL生成 | window.URL.createObjectURLで一時URLを生成 | normalizeAsset |
| BR-43-06 | UUID v4 | メディアファイルIDはuuid v4で生成 | normalizeAsset |

### 計算ロジック

- ページカウント: `Math.floor(count / pageSize)`
- カーソルアクション: index < pageCount なら'next','last'、index > 0 なら'prev','first'

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

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

データベースは使用しない。全てブラウザのwindowオブジェクト上で管理。

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| getFile | window.repoFiles | READ | パスセグメントでツリーを走査して取得 |
| writeFile | window.repoFiles | WRITE | パスセグメントでツリーを構築して書き込み |
| deleteFile | window.repoFiles | DELETE | lodash.unsetでパスを削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | EditorialWorkflowError | unpublishedEntryで対象が存在しない場合 | content is not under editorial workflow |
| - | ObjectURL生成失敗 | createObjectURLが失敗した場合 | 空文字列をURLとして使用 |

### リトライ仕様

インメモリ操作のためリトライは不要。

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

同期的なJavaScript操作のため、トランザクション制御は不要。

## パフォーマンス要件

インメモリ操作のため、応答時間は即時。ただし大量のテストデータではメモリ使用量に注意。

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

- テスト/デモ用途のため、セキュリティ要件は低い
- 認証はバイパスされる
- ページリロードでデータが消失する（永続化なし）

## 備考

- `isGitBackend()`は`false`を返す
- `getDeployPreview()`は常に`null`を返す
- ファイルツリーはネストされたオブジェクト構造で管理される（例: `{ content: { posts: { 'entry.md': { content: '...', path: '...' } } } }`）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | implementation.ts | `packages/decap-cms-backend-test/src/implementation.ts` | RepoFile型（29行目）、RepoTree型（30行目）、Diff型（32-39行目）、UnpublishedRepoEntry型（41-47行目） |

**読解のコツ**: `RepoTree`は再帰的な型定義で、各キーが`RepoFile`か別の`RepoTree`を持つ。これがファイルシステムのディレクトリ構造をシミュレートしている。`window.repoFiles`がこの型のインスタンスとなる。

#### Step 2: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | implementation.ts | `packages/decap-cms-backend-test/src/implementation.ts` | getFile（59-66行目）、writeFile（68-77行目）、deleteFile（79-81行目）、getFolderFiles（104-129行目） |

**主要処理フロー**:
- **59-66行目**: `getFile()` - パスを'/'で分割してツリーを順次辿る
- **68-77行目**: `writeFile()` - パスの各セグメントでオブジェクトを作成し最後にファイルを書き込む
- **79-81行目**: `deleteFile()` - lodashの`unset`でパスを削除
- **104-129行目**: `getFolderFiles()` - 再帰的にフォルダ内のファイルを収集（拡張子フィルタ・深度制限付き）

#### Step 3: ページネーション機能

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | implementation.ts | `packages/decap-cms-backend-test/src/implementation.ts` | getCursor（85-102行目）、traverseCursor（168-200行目） |

**主要処理フロー**:
- **83行目**: `pageSize = 10` - 定数定義
- **85-102行目**: `getCursor()` - ページネーションメタデータとアクション（next/prev/first/last）を含むCursorオブジェクト生成
- **168-200行目**: `traverseCursor()` - カーソルアクションに基づいてページ移動

#### Step 4: エントリCRUD操作

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | implementation.ts | `packages/decap-cms-backend-test/src/implementation.ts` | entriesByFolder（202-214行目）、persistEntry（310-337行目）、publishUnpublishedEntry（344-368行目） |

**主要処理フロー**:
- **310-337行目**: `persistEntry()` - useWorkflowフラグで保存先を切り替え。通常はrepoFilesに、ワークフロー時はrepoFilesUnpublishedに保存
- **344-368行目**: `publishUnpublishedEntry()` - diffsの各エントリをrepoFilesに反映。originalPath存在時はリネーム処理も実行

#### Step 5: メディア管理とAsset正規化

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | implementation.ts | `packages/decap-cms-backend-test/src/implementation.ts` | normalizeAsset（397-413行目）、persistMedia（415-421行目） |

**主要処理フロー**:
- **397-413行目**: `normalizeAsset()` - AssetProxyからFileObjを取得し、ObjectURLを生成、uuid v4でIDを付与
- **400行目**: `attempt(window.URL.createObjectURL, fileObj)` - lodashのattemptでエラーハンドリング

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

```
TestBackend
    |
    +-- entriesByFolder(folder, extension, depth)
    |       +-- getFolderFiles(window.repoFiles, folder, extension, depth)
    |       +-- getCursor(folder, extension, entries, 0, depth)
    |
    +-- persistEntry(entry, options)
    |       +-- [useWorkflow] addOrUpdateUnpublishedEntry()
    |       +-- [通常] writeFile() × dataFiles
    |
    +-- publishUnpublishedEntry(collection, slug)
    |       +-- getFolderFiles() [リネーム対象検索]
    |       +-- deleteFile() + writeFile() [リネーム]
    |       +-- writeFile() [データ書き込み]
    |
    +-- getMedia(mediaFolder)
    |       +-- getFolderFiles()
    |       +-- normalizeAsset() × files
    |
    +-- persistMedia(assetProxy)
            +-- normalizeAsset()
            +-- writeFile()
```

### データフロー図

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

CMS操作            ──▶  TestBackend メソッド        ──▶  window.repoFiles
(エントリ/メディア)       getFile/writeFile/deleteFile      (インメモリツリー)

window.repoFiles   ──▶  getFolderFiles()           ──▶  entries + Cursor
(インメモリツリー)        拡張子フィルタ + ページネーション    (Redux Store)

AssetProxy         ──▶  normalizeAsset()           ──▶  {id, name, url, ...}
(ファイルオブジェクト)     createObjectURL + uuid v4        (メディアファイル情報)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| implementation.ts | `packages/decap-cms-backend-test/src/implementation.ts` | ソース | TestBackendクラスの全実装 |
| index.ts | `packages/decap-cms-backend-test/src/index.ts` | ソース | パッケージエントリポイント |
| AuthenticationPage.js | `packages/decap-cms-backend-test/src/AuthenticationPage.js` | ソース | テスト用認証ページ（ログインスキップ対応） |
| implementation.spec.js | `packages/decap-cms-backend-test/src/__tests__/implementation.spec.js` | テスト | TestBackendのユニットテスト |
