# 機能設計書 39-Azure DevOpsバックエンド

## 概要

Azure DevOps Gitリポジトリを使用したCMSバックエンドについて記述する。Azure DevOps REST APIを通じてエントリの読み書き・メディア管理・エディトリアルワークフローを実行する。

### 本機能の処理概要

Azure DevOpsバックエンドはDecap CMSのImplementationインターフェースを実装するクラスで、Azure DevOps GitリポジトリをCMSのデータストアとして使用する。リポジトリは「org/project/repo」の3段階パスで指定し、Azure DevOps REST APIを通じてコンテンツ操作を行う。

**業務上の目的・背景**：Microsoft Azure DevOpsをCI/CDパイプラインとして使用する企業向けのCMSバックエンド。Azure ADによる認証と、Azure DevOpsのGitリポジトリによるコンテンツ管理を統合する。

**機能の利用シーン**：config.ymlでbackend.name: 'azure'と設定されている場合に使用。Azure DevOpsでホスティングされたGitリポジトリ上のコンテンツを管理する。

**主要な処理内容**：
1. トークンベース認証
2. エントリのフォルダ/ファイルベース読み込み
3. エントリの保存・削除
4. メディアファイルの一覧取得・アップロード・削除
5. エディトリアルワークフロー（PRベース）
6. デプロイプレビュー

**関連システム・外部連携**：Azure DevOps REST API（dev.azure.com）

**権限による制御**：Azure DevOpsのリポジトリ権限に基づく。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ログイン画面 | 主機能 | Azure DevOps認証によるログイン |

## 機能種別

データ連携 / CRUD操作 / 外部API連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config.backend.repo | string | Yes | リポジトリ（org/project/repo形式） | 3パート必須、未設定・形式不正時はエラー |
| config.backend.branch | string | No | ブランチ名（デフォルト: 'master'） | なし |
| config.backend.api_root | string | No | API Root（デフォルト: 'https://dev.azure.com'） | なし |
| config.backend.api_version | string | No | APIバージョン（デフォルト: '6.1-preview'） | なし |
| config.backend.squash_merges | boolean | No | スカッシュマージ使用 | なし |
| config.backend.cms_label_prefix | string | No | CMSラベルのプレフィックス | なし |
| config.backend.preview_context | string | No | デプロイプレビューのコンテキスト | なし |

### 入力データソース

- config.yml（バックエンド設定）
- Azure DevOps API（リポジトリデータ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| entries | ImplementationEntry[] | エントリデータ |
| media | MediaFile[] | メディアファイル一覧（blobUrl付き） |
| user | User | 認証ユーザー情報 |

### 出力先

- Reduxストア経由でUI表示
- Azure DevOps API（保存時）

## 処理フロー

### 処理シーケンス

```
1. 初期化
   ├─ repoのパース（org/project/repoName分割）
   ├─ 3パート形式のバリデーション
   └─ asyncLock初期化
2. 認証
   ├─ トークン設定
   ├─ APIインスタンス生成
   └─ ユーザー情報取得
3. エントリ操作
   ├─ entriesByFolder: ファイル一覧 → 拡張子フィルタ → エントリ読み込み
   ├─ entriesByFiles: ファイル指定読み込み
   └─ persistEntry: ファイル保存
4. メディア操作
   ├─ getMedia: ファイル一覧 → blobUrl取得
   └─ persistMedia: SHA計算 → ファイル保存
5. ワークフロー操作
   └─ PRベースのステータス管理（ロック付き）
```

### フローチャート

```mermaid
flowchart TD
    A[Azure Backend初期化] --> B[parseAzureRepo]
    B --> C{3パート形式?}
    C -->|Yes| D[authenticate]
    C -->|No| E[エラー: 形式不正]
    D --> F[API生成]
    F --> G[api.user]
    G --> H[ユーザー情報返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-01 | リポジトリパース | repoは「org/project/repoName」の3パート形式必須 | 初期化時 |
| BR-39-02 | メディアフォルダトリム | mediaFolderの前後のスラッシュを除去 | 初期化時 |
| BR-39-03 | ロック付き操作 | updateStatus/deleteUnpublished/publishはrunWithLockで排他制御 | ワークフロー操作時 |
| BR-39-04 | メディアblobUrl | getMediaでは各ファイルのblobUrlをgetMediaDisplayURLで取得して返却 | getMedia時 |

### 計算ロジック

parseAzureRepo: `repo.split('/') → [org, project, repoName]`

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

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

| 操作 | 対象 | 操作種別 | 概要 |
|-----|------|---------|------|
| persistEntry | Azure DevOpsリポジトリ | CREATE/UPDATE | APIでファイルをコミット |
| deleteFiles | Azure DevOpsリポジトリ | DELETE | コミットメッセージ付きファイル削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 設定エラー | repoが未設定またはstring以外 | "The Azure backend needs a 'repo'" |
| - | 設定エラー | repoが3パート形式でない | "The Azure backend must be in a the format of {org}/{project}/{repo}" |

### リトライ仕様

特になし

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

asyncLock（runWithLock）によるCMSレベルの排他制御

## パフォーマンス要件

- MAX_CONCURRENT_DOWNLOADS = 10

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

- トークンベース認証
- TODO: 実際のlogoutはAzure AD OAuth2 logoutエンドポイントへのリダイレクトが必要（未実装）

## 備考

- APIバージョンとして '6.1-preview' がデフォルト
- logoutは現在トークンの破棄のみ（Azure AD OAuthログアウトは未実装: TODO）
- getMediaではblobUrlを各ファイルに直接取得して返却する（他のバックエンドとは異なるパターン）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | implementation.ts | `packages/decap-cms-backend-azure/src/implementation.ts` | Azureクラスのプロパティ（62-82行目）: repo（org/project/repoName構造体）、apiVersion。parseAzureRepo関数（42-60行目）: リポジトリパス解析 |

**読解のコツ**: Azure DevOpsはリポジトリパスがorg/project/repoの3段階構造で、他のGitバックエンドとは異なる点に注意。

#### Step 2: エントリーポイント

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | implementation.ts | `packages/decap-cms-backend-azure/src/implementation.ts` | コンストラクタ（84-100行目）: parseAzureRepo呼び出し、apiVersion設定。authenticate（126-143行目）: API生成、ユーザー取得 |

**主要処理フロー**:
1. **42-60行目**: parseAzureRepo - repoを'/'で分割し3パート検証
2. **84-100行目**: コンストラクタ。media_folderをtrimで前後スラッシュ除去
3. **126-143行目**: authenticate。API生成にapiVersionを渡す
4. **159-176行目**: entriesByFolder。filterByExtensionでpath付きフィルタリング
5. **195-204行目**: getMedia。各ファイルにblobUrlを取得して返却

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

```
Azure (implementation.ts)
    │
    ├─ parseAzureRepo(config) → { org, project, repoName }
    │
    ├─ authenticate(state)
    │      ├─ new API(config, token)
    │      └─ api.user()
    │
    ├─ entriesByFolder(folder, extension, depth)
    │      ├─ api.listFiles(folder, depth > 1)
    │      ├─ filterByExtension({ path }, extension)
    │      └─ entriesByFolder(listFiles, readFile, ...)
    │
    ├─ getMedia()
    │      ├─ api.listFiles(mediaFolder, false)
    │      └─ getMediaDisplayURL({ id, path }) [各ファイル]
    │
    └─ persistEntry(entry, options)
           └─ api.persistFiles(dataFiles, mediaFiles, options)
```

### データフロー図

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

config.yml            ─▶ parseAzureRepo()            ─▶ { org, project, repoName }
  backend:
    name: azure
    repo: org/project/repo

Token                 ─▶ authenticate()              ─▶ User { token, ...user }
                          └─ new API(config, token)

folder, extension     ─▶ entriesByFolder()           ─▶ entries[]
                          ├─ api.listFiles()
                          └─ api.readFile()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| implementation.ts | `packages/decap-cms-backend-azure/src/implementation.ts` | ソース | Azure DevOpsバックエンドのImplementation実装 |
| API.ts | `packages/decap-cms-backend-azure/src/API.ts` | ソース | Azure DevOps REST API操作 |
| AuthenticationPage.js | `packages/decap-cms-backend-azure/src/AuthenticationPage.js` | ソース | Azure認証画面コンポーネント |
| index.ts | `packages/decap-cms-backend-azure/src/index.ts` | ソース | パッケージエントリーポイント |
