# 機能設計書 32-Uploadcare連携

## 概要

Uploadcareメディアライブラリとの統合機能について記述する。Uploadcareのファイルアップロードウィジェットを利用し、メディアファイルのアップロードおよびエントリへの挿入を行う。

### 本機能の処理概要

Uploadcare連携は、Uploadcareのファイルアップロード・管理サービスをDecap CMSのエディタウィジェットと統合する機能である。エディタのファイル/画像フィールドからUploadcareウィジェットを呼び出し、ファイルのアップロードや既存ファイルの選択を可能にする。

**業務上の目的・背景**：メディアファイルの管理と最適化を外部サービスに委譲することで、Gitリポジトリに大きなバイナリファイルを保存する必要をなくす。Uploadcareは画像変換、CDN配信、ファイル管理を提供し、コンテンツ編集者のワークフローを効率化する。

**機能の利用シーン**：エントリ編集画面でファイルウィジェットや画像ウィジェットを操作する際に利用される。フィールドに設定されたメディアライブラリがUploadcareの場合、フィールドのメディア選択ボタンを押すとUploadcareダイアログが表示される。既存のアップロード済みファイルの再選択やグループファイルの管理にも対応する。

**主要な処理内容**：
1. Uploadcareウィジェットの初期化とpublicKey設定
2. エフェクトタブの登録（画像編集機能）
3. 既存のURL値からUploadcareファイル/グループオブジェクトへの変換
4. UploadcareダイアログのオープンとURL構築
5. 単一/複数ファイルの選択とハンドリング

**関連システム・外部連携**：Uploadcareウィジェット（uploadcare-widget npm パッケージ）およびUploadcare CDN（`https://ucarecdn.com`）との連携。

**権限による制御**：特になし。UploadcareのAPI権限はpublicKeyで管理される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 9 | メディアライブラリ画面 | 参照画面 | Uploadcare連携時のメディアアップロードダイアログ表示 |

## 機能種別

外部連携 / メディア管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| options.config.publicKey | string | Yes | UploadcareのpublicKey | なし |
| options.config.previewStep | boolean | No | プレビューステップの表示（デフォルト: true） | なし |
| options.config.multiple | boolean | No | 複数ファイル選択の許可 | なし |
| options.config.imagesOnly | boolean | No | 画像のみのフィルタ | なし |
| options.settings.defaultOperations | string | No | デフォルトの画像変換オペレーション（"/"で開始すべき） | "/"で始まるか検証（警告のみ） |
| options.settings.autoFilename | boolean | No | ファイル名の自動付与 | なし |
| handleInsert | Function | Yes | メディア選択時のコールバック | なし |
| value | string / string[] / Iterable | No | 既存のファイルURL（showメソッドで使用） | なし |

### 入力データソース

- `config.yml` のmedia_libraryセクション（Uploadcare設定）
- Uploadcare CDN（アップロード済みファイル）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| url | string / string[] | 選択されたファイルのCDN URL（単一または配列） |

### 出力先

- エントリのメディアフィールド（handleInsertコールバック経由）

## 処理フロー

### 処理シーケンス

```
1. init関数の実行
   └─ config.ymlのmedia_library設定を受け取る
2. publicKeyの設定
   └─ window.UPLOADCARE_PUBLIC_KEY に設定
3. エフェクトタブの登録
   └─ uploadcare.registerTab('preview', uploadcareTabEffects)
4. APIオブジェクトの返却（show, enableStandalone）
5. showメソッド呼び出し時
   ├─ 既存値からUploadcareファイルオブジェクトの取得
   │   ├─ isFileGroup判定（URLパターンによるグループ検出）
   │   ├─ getFileGroup（グループIDの抽出とloadFileGroup）
   │   └─ getFile（個別ファイルのfileFrom処理）
   ├─ 設定のマージとmultiple制御
   └─ openDialog呼び出し
6. openDialog内
   ├─ uploadcare.openDialog実行
   ├─ グループ/単一ファイルの判定
   ├─ buildUrlでCDN URLの構築
   │   ├─ defaultOperationsの適用
   │   └─ autoFilenameの適用
   └─ handleInsertにURLを渡す
```

### フローチャート

```mermaid
flowchart TD
    A[init呼び出し] --> B[publicKey設定]
    B --> C[エフェクトタブ登録]
    C --> D[APIオブジェクト返却]
    D --> E{show呼び出し?}
    E -->|Yes| F[getFiles: 既存値をファイルオブジェクトに変換]
    F --> G{配列 or Iterable?}
    G -->|Yes| H{isFileGroup?}
    H -->|Yes| I[getFileGroup: グループID抽出]
    H -->|No| J[getFile: 各URLをfileFromで変換]
    G -->|No| K[getFile: 単一URLを変換]
    I --> L[openDialog]
    J --> L
    K --> L
    L --> M{グループ?}
    M -->|Yes| N[各ファイルのbuildUrl]
    M -->|No| O[単一ファイルのbuildUrl]
    N --> P[handleInsert: URL配列]
    O --> Q[handleInsert: 単一URL]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-32-01 | ファイルグループ判定 | URLパターン `~N/nth/i/` に一致する場合、ファイルグループとして扱う | 既存値が配列の場合 |
| BR-32-02 | 複数選択制御 | allowMultipleがfalseの場合、configのmultipleをfalseに設定 | showメソッド呼び出し時 |
| BR-32-03 | defaultOperations形式 | "/"で始まらない場合はコンソール警告を表示 | defaultOperationsが設定されている場合 |
| BR-32-04 | autoFilename適用 | CDN URLが"/"で終わる場合にファイル名を自動付与 | autoFilenameが有効でURLが"/"で終わる場合 |
| BR-32-05 | スタンドアロン無効 | enableStandalone()はfalseを返す（既存ファイル一覧表示非対応） | 常時 |

### 計算ロジック

URL構築ロジック:
- `cdnUrl` + （isImageかつdefaultOperationsあり場合: `-` + `defaultOperations`）
- URL末尾が`/`でautoFilenameが有効の場合: URL + `name`

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

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

データベース操作なし。ファイル管理はUploadcare側で行われる。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 警告 | defaultOperationsが"/"で始まらない場合 | コンソール警告。正しい形式の例を表示 |
| - | アップロードエラー | Uploadcareへのファイルアップロードが失敗した場合 | Uploadcareウィジェット側でエラーハンドリング |
| - | グループID抽出エラー | 不正なグループURLの場合 | 正規表現マッチ失敗によるランタイムエラー |

### リトライ仕様

リトライ機構はUploadcareウィジェット側で管理される。

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

トランザクション管理なし（外部サービス連携のため）

## パフォーマンス要件

- Uploadcareウィジェットはnpmパッケージとしてバンドルされるため、外部スクリプトの動的読み込みは不要
- グローバル設定: `window.UPLOADCARE_LIVE = false`, `window.UPLOADCARE_MANUAL_START = true`

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

- publicKeyはクライアント側で使用されるため、公開可能な設定値である
- Uploadcareのファイルは `https://ucarecdn.com` 経由でHTTPS配信される

## 備考

- enableStandalone()がfalseを返すため、グローバルナビの「Media」ボタンからは利用不可（エディタフィールドからのみ利用可能）
- Immutable.jsのIterableにも対応（value入力時）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.js | `packages/decap-cms-media-library-uploadcare/src/index.js` | グローバル設定（5-6行目）: UPLOADCARE_LIVE=false, UPLOADCARE_MANUAL_START=true。CDN_BASE_URL（9行目）: Uploadcare CDNのベースURL |

**読解のコツ**: Uploadcareウィジェットはnpmパッケージ（uploadcare-widget）としてインポートされ、jQuery依存のpromiseオブジェクトを返す。`state`メソッドの有無でjQuery promiseとネイティブPromiseを区別している点に注意。

#### Step 2: ファイル/グループ解決ロジック

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.js | `packages/decap-cms-media-library-uploadcare/src/index.js` | isFileGroup関数（24-33行目）: URLパターンからファイルグループを判定。getFileGroup関数（38-50行目）: グループIDの抽出。getFiles関数（57-63行目）: 値の型に応じた変換分岐。getFile関数（70-73行目）: 個別URLからファイルオブジェクトへの変換 |

**主要処理フロー**:
- **24-33行目**: isFileGroup - URLパターン `~N/nth/i/` の正規表現マッチングでグループ判定
- **38-50行目**: getFileGroup - 最初のURLからグループIDを正規表現で抽出し、uploadcare.loadFileGroupで読み込み
- **57-63行目**: getFiles - 配列/Iterable/文字列/nullの4パターンで分岐
- **70-73行目**: getFile - CDNベースURLで始まりグループパターンに一致しない場合は'uploaded'、それ以外は'url'でfileFrom

#### Step 3: ダイアログとURL構築

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.js | `packages/decap-cms-media-library-uploadcare/src/index.js` | openDialog関数（80-114行目）: ダイアログのオープンとURL構築。init関数（120-174行目）: 初期化とAPIオブジェクト返却 |

**主要処理フロー**:
- **81-85行目**: defaultOperationsの形式チェック（"/"で始まるか）
- **87-99行目**: buildUrl - CDN URLの構築。defaultOperationsとautoFilename適用
- **101-113行目**: uploadcare.openDialog実行。グループ/単一の判定とhandleInsert呼び出し
- **120-124行目**: init - publicKey設定とbaseConfig構築
- **130行目**: エフェクトタブの登録
- **137-163行目**: showメソッド - 設定マージ、getFiles、openDialog呼び出し
- **172行目**: enableStandalone - falseを返す

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

```
CMS MediaLibrary制御
    │
    ├─ uploadcareMediaLibrary.init({ options, handleInsert })
    │      ├─ window.UPLOADCARE_PUBLIC_KEY = publicKey
    │      ├─ uploadcare.registerTab('preview', uploadcareTabEffects)
    │      └─ return { show, enableStandalone }
    │
    └─ show({ value, config, allowMultiple, imagesOnly })
           ├─ getFiles(value)
           │      ├─ isFileGroup(arr) → getFileGroup(arr)
           │      │                        └─ uploadcare.loadFileGroup(groupId)
           │      └─ getFile(url)
           │             └─ uploadcare.fileFrom(source, url)
           └─ openDialog({ files, config, handleInsert, settings })
                  ├─ uploadcare.openDialog(files, config)
                  ├─ buildUrl(fileInfo)
                  └─ handleInsert(url / urls)
```

### データフロー図

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

config.yml            ─▶ init()                      ─▶ APIオブジェクト
  media_library:          ├─ publicKey設定                 { show,
    name: uploadcare      └─ エフェクトタブ登録               enableStandalone }
    config:
      publicKey: xxx

既存フィールド値       ─▶ show()                      ─▶ Uploadcareダイアログ表示
  (URL文字列/配列)        ├─ getFiles(value)
                          └─ openDialog()

ユーザー選択           ─▶ openDialog().done()         ─▶ handleInsert(url)
  (Uploadcareダイアログ)    ├─ buildUrl(fileInfo)          ─▶ エントリフィールドに挿入
                            └─ グループ/単一判定
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.js | `packages/decap-cms-media-library-uploadcare/src/index.js` | ソース | Uploadcare連携のメイン実装。init/show/enableStandaloneの定義 |
| index.spec.js | `packages/decap-cms-media-library-uploadcare/src/__tests__/index.spec.js` | テスト | Uploadcare連携のテスト |
| mediaLibrary.ts | `packages/decap-cms-core/src/mediaLibrary.ts` | ソース | 外部メディアライブラリの初期化・表示制御の上位モジュール |
| registry.js | `packages/decap-cms-core/src/lib/registry.js` | ソース | メディアライブラリの登録管理（registerMediaLibrary） |
