# 機能設計書 51-デプロイプレビュー

## 概要

本ドキュメントは、Decap CMSにおけるデプロイプレビュー機能の設計を記述する。エントリの公開前確認用プレビューURLの取得と表示を担う機能である。

### 本機能の処理概要

**業務上の目的・背景**：CMSで編集したコンテンツが実際のWebサイト上でどのように表示されるかを、公開前に確認したいという要求に応える機能である。Netlify等のホスティングサービスが提供するデプロイプレビュー（ブランチデプロイ）のURLを取得し、エディタ画面上にリンクとして表示することで、編集者が視覚的にコンテンツの仕上がりを確認できる。

**機能の利用シーン**：エントリ編集画面において、編集中のエントリや未公開のエントリに対してデプロイプレビューURLが存在する場合に、ツールバー上にプレビューリンクが表示される。公開済みエントリの場合は`getDeploy`、未公開エントリの場合は`getDeployPreview`がそれぞれ呼び出される。

**主要な処理内容**：
1. バックエンドAPIを通じてデプロイプレビュー情報（URL・ステータス）を取得する
2. 取得状態をReduxストアで管理し、ローディング・成功・失敗の3状態を遷移させる
3. 重複リクエストの防止（同一エントリへの並行フェッチを抑止する）
4. エラー発生時にトースト通知を表示する

**関連システム・外部連携**：各バックエンド（GitHub、GitLab等）が提供するデプロイプレビューAPI。Netlify等のホスティングサービスのデプロイステータスAPIと間接的に連携する。

**権限による制御**：特に権限による制御はない。デプロイプレビューの利用可否はバックエンドの`getDeployPreview`メソッドの実装有無に依存する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 6 | エントリ編集画面 | 補助機能 | エントリのデプロイプレビューURL取得と表示。loadDeployPreviewアクションによるプレビュー状態確認 |

## 機能種別

データ連携（外部デプロイサービスからのプレビューURL取得）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| collection | Collection (ImmutableMap) | Yes | 対象コレクション | - |
| slug | string | Yes | エントリのスラッグ | - |
| entry | Entry (ImmutableMap) | Yes | 対象エントリオブジェクト | - |
| published | boolean | Yes | 公開済みかどうかのフラグ | - |
| opts.maxAttempts | number | No | 最大リトライ回数 | - |
| opts.interval | number | No | リトライ間隔（ミリ秒） | - |

### 入力データソース

- Reduxストア（`state.config`からバックエンドインスタンスを取得）
- Reduxストア（`state.deploys`から現在のフェッチ状態を取得）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| url | string / undefined | デプロイプレビューのURL |
| status | string | デプロイのステータス（例: "SUCCESS", "BUILDING"等） |
| isFetching | boolean | フェッチ中かどうかのフラグ |

### 出力先

Reduxストア（`state.deploys`）にキー`{collection}.{slug}`で格納される。

## 処理フロー

### 処理シーケンス

```
1. loadDeployPreview関数が呼び出される
   └─ 引数: collection, slug, entry, published, opts
2. 現在のReduxストアからデプロイ状態を取得
   └─ selectDeployPreview(state, collectionName, slug)
3. フェッチ中か確認（重複防止）
   └─ deployState.isFetchingがtrueなら処理中断
4. DEPLOY_PREVIEW_REQUESTアクションをディスパッチ
   └─ isFetching = trueに設定
5. バックエンドAPIを呼び出し
   └─ published=true: backend.getDeploy(collection, slug, entry)
   └─ published=false: backend.getDeployPreview(collection, slug, entry, opts)
6a. 成功時: DEPLOY_PREVIEW_SUCCESSアクションをディスパッチ
   └─ url, statusを格納、isFetching = false
6b. 結果なし: DEPLOY_PREVIEW_FAILUREアクションをディスパッチ
   └─ isFetching = false
6c. エラー時: エラー通知を表示 + DEPLOY_PREVIEW_FAILUREアクションをディスパッチ
```

### フローチャート

```mermaid
flowchart TD
    A[loadDeployPreview呼び出し] --> B{現在フェッチ中?}
    B -->|Yes| C[処理中断]
    B -->|No| D[DEPLOY_PREVIEW_REQUEST ディスパッチ]
    D --> E{published?}
    E -->|Yes| F[backend.getDeploy]
    E -->|No| G[backend.getDeployPreview]
    F --> H{結果あり?}
    G --> H
    H -->|Yes| I[DEPLOY_PREVIEW_SUCCESS ディスパッチ]
    H -->|No| J[DEPLOY_PREVIEW_FAILURE ディスパッチ]
    F -.->|例外| K[エラー通知 + DEPLOY_PREVIEW_FAILURE]
    G -.->|例外| K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-51-01 | 重複フェッチ防止 | 同一エントリに対して既にフェッチが進行中の場合は新たなリクエストを発行しない | isFetchingがtrueの時 |
| BR-51-02 | 公開/未公開の分岐 | 公開済みエントリはgetDeploy、未公開エントリはgetDeployPreviewを呼び出す | publishedフラグによる |
| BR-51-03 | エラー通知 | API呼び出し失敗時は8秒間のエラー通知をトースト表示する | catchブロック到達時 |

### 計算ロジック

Reduxストアのキーは`{collection}.{slug}`形式で生成される。

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

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

本機能はデータベースを直接操作しない。Reduxストア（インメモリ状態管理）のみを更新する。

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| loadDeployPreview | state.deploys | UPDATE | デプロイプレビュー状態の更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | API通信エラー | バックエンドAPI呼び出し失敗 | コンソールにエラーログ出力、トースト通知（8秒後自動消去） |
| ui.toast.onFailToLoadDeployPreview | 通知キー | デプロイプレビュー取得失敗 | ローカライズされたエラーメッセージを表示 |

### リトライ仕様

`opts`パラメータで`maxAttempts`と`interval`を指定可能。リトライロジックはバックエンドの`getDeployPreview`メソッド内で実装される。

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

該当なし（Reduxストアの更新は単一ディスパッチで完結する）。

## パフォーマンス要件

重複リクエスト防止機構により、同一エントリへの並行リクエストが発生しないよう制御している。

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

デプロイプレビューURLはバックエンドAPI経由で取得されるため、認証情報はバックエンドレイヤーで管理される。

## 備考

- デプロイプレビューの具体的な取得ロジック（ポーリング等）はバックエンド実装に依存する
- GitHub Deployments API、Netlify Deploy API等のサービス固有の実装がバックエンドパッケージに含まれる

---

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

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

### 推奨読解順序

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

Reduxストアで管理されるデプロイプレビューの状態型を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | deploys.ts (reducer) | `packages/decap-cms-core/src/reducers/deploys.ts` | Deploys型定義（11-17行目）: isFetching, url, statusの3フィールド構造 |

**読解のコツ**: Immerの`produce`を使ったイミュータブル状態更新パターンが使われている。`state`を直接変更しているように見えるが、Immerがプロキシで変更を検出し新しいオブジェクトを生成している。

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

デプロイプレビュー取得のアクション定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | deploys.ts (action) | `packages/decap-cms-core/src/actions/deploys.ts` | loadDeployPreview関数（53-100行目）がメインのエントリーポイント |

**主要処理フロー**:
1. **9-11行目**: 3つのアクションタイプ定数の定義（REQUEST, SUCCESS, FAILURE）
2. **13-21行目**: deployPreviewLoading - フェッチ開始アクション
3. **23-38行目**: deployPreviewLoaded - フェッチ成功アクション（url, statusを含む）
4. **40-48行目**: deployPreviewError - フェッチ失敗アクション
5. **53-100行目**: loadDeployPreview - 非同期サンクアクション
6. **66-69行目**: isFetchingチェックによる重複防止
7. **78-80行目**: published分岐（getDeploy vs getDeployPreview）
8. **85-98行目**: エラーハンドリング（通知 + エラーアクション）

#### Step 3: リデューサーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | deploys.ts (reducer) | `packages/decap-cms-core/src/reducers/deploys.ts` | 3つのアクションタイプに対するステート遷移（21-46行目） |

**主要処理フロー**:
- **23-28行目**: REQUEST - キーを生成し`isFetching = true`に設定
- **30-36行目**: SUCCESS - url, statusを格納し`isFetching = false`に設定
- **38-43行目**: FAILURE - `isFetching = false`のみ設定
- **48-50行目**: selectDeployPreview - セレクター関数

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

```
loadDeployPreview (actions/deploys.ts)
    |
    +-- selectDeployPreview (reducers/deploys.ts)
    |       └── 現在のフェッチ状態を確認
    |
    +-- deployPreviewLoading (actions/deploys.ts)
    |       └── dispatch(DEPLOY_PREVIEW_REQUEST)
    |
    +-- currentBackend (backend.ts)
    |       +-- backend.getDeploy (published)
    |       └── backend.getDeployPreview (unpublished)
    |
    +-- deployPreviewLoaded (actions/deploys.ts)
    |       └── dispatch(DEPLOY_PREVIEW_SUCCESS)
    |
    +-- deployPreviewError (actions/deploys.ts)
    |       └── dispatch(DEPLOY_PREVIEW_FAILURE)
    |
    └── addNotification (actions/notifications.ts)
            └── エラー時のトースト通知
```

### データフロー図

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

collection,slug,     loadDeployPreview              Reduxストア
entry,published  ───> (Thunkアクション) ───>  state.deploys[collection.slug]
                         |                          {isFetching, url, status}
                         v
                   Backend API呼び出し
                   getDeploy /
                   getDeployPreview
                         |
                         v
                   {url, status}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| deploys.ts | `packages/decap-cms-core/src/actions/deploys.ts` | ソース | アクション定義（loadDeployPreview等） |
| deploys.ts | `packages/decap-cms-core/src/reducers/deploys.ts` | ソース | リデューサー定義（状態管理・セレクター） |
| notifications.ts | `packages/decap-cms-core/src/actions/notifications.ts` | ソース | エラー通知アクション |
| index.ts | `packages/decap-cms-core/src/reducers/index.ts` | ソース | ルートリデューサー（deploysの統合） |
| globalUI.ts | `packages/decap-cms-core/src/reducers/globalUI.ts` | ソース | デプロイプレビュー状態の再エクスポート |
