# 画面設計書 84-フィーチャーフラグ編集

## 概要

本ドキュメントは、GitLabのフィーチャーフラグ編集画面の設計仕様を定義するものである。

### 本画面の処理概要

フィーチャーフラグ編集画面は、既存のフィーチャーフラグの設定を変更するための画面である。フィーチャーフラグの名前、説明、有効/無効状態、およびロールアウト戦略を編集できる。

**業務上の目的・背景**：運用中のフィーチャーフラグの設定を変更する必要が生じた場合（ロールアウト比率の調整、対象環境の変更、名前や説明の修正など）、この画面から編集を行う。特に段階的リリースにおいて、パーセンテージを徐々に上げていく際や、問題発生時に即座に無効化する際に頻繁に利用される。また、画面上部のトグルスイッチにより、フォーム送信せずに即座に有効/無効を切り替えることも可能。

**画面へのアクセス方法**：フィーチャーフラグ一覧画面から編集ボタン（鉛筆アイコン）をクリック、またはURL直接アクセス。URLパターンは `/:namespace/:project/-/feature_flags/:iid/edit` となる。

**主要な操作・処理内容**：
1. フィーチャーフラグの有効/無効即時切り替え（トグル）
2. フィーチャーフラグ名の変更
3. 説明の変更
4. ロールアウト戦略の追加・変更・削除
5. 環境スコープの変更
6. 変更の保存

**画面遷移**：
- 遷移元：フィーチャーフラグ一覧画面
- 遷移先：フィーチャーフラグ一覧画面（保存成功時）、同一画面（エラー時）

**権限による表示制御**：
- `update_feature_flag`権限がない場合はアクセス不可
- EE機能の「FeatureFlagActions」コンポーネントは条件付きで表示

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 59 | フィーチャーフラグ | 主機能 | フィーチャーフラグの編集 |

## 画面種別

編集

## URL/ルーティング

```
GET /:namespace/:project/-/feature_flags/:iid/edit
GET /:namespace/:project/-/feature_flags/:iid.json (データ取得API)
PUT /:namespace/:project/-/feature_flags/:iid.json (更新API)
```

## 入出力項目

### 入力項目

| 項目名 | データ型 | 必須 | バリデーション | 説明 |
|--------|----------|------|----------------|------|
| Active | Boolean | - | - | フィーチャーフラグの有効/無効 |
| Name | String | 必須 | 2-63文字、フォーマット制約 | フィーチャーフラグの識別名 |
| Description | String | - | 最大255文字 | フィーチャーフラグの説明 |
| Strategies | Array<Strategy> | - | - | ロールアウト戦略の配列 |

### 戦略（Strategy）項目

| 項目名 | データ型 | 必須 | 説明 |
|--------|----------|------|------|
| id | Integer | - | 既存戦略のID（更新時） |
| name | String | 必須 | 戦略タイプ |
| parameters | Object | - | 戦略パラメータ |
| scopes | Array<Scope> | - | 適用環境スコープ |
| _destroy | Boolean | - | 削除フラグ（true時に削除） |

## 表示項目

### ヘッダー情報

| 項目名 | データ型 | 説明 | ソース |
|--------|----------|------|--------|
| タイトル | String | "^{iid} {name}" または "Edit {name}" | iid, name |
| ステータストグル | Boolean | 有効/無効の即時切り替えトグル | active |

## イベント仕様

### 1-有効/無効即時切り替え

**トリガー**: ステータストグルの操作

**処理フロー**:
1. Vuexアクション`toggleActive`を実行
2. PUTリクエストでactive状態のみ更新
3. 成功時：トグル状態が反映される
4. 失敗時：トグル状態が戻る

**遷移先**: 画面内更新

### 2-変更保存

**トリガー**: 「Save changes」ボタン押下

**処理フロー**:
1. フォームデータを収集
2. Vuexアクション`updateFeatureFlag`を実行
3. PUTリクエストを送信
4. 成功時：フィーチャーフラグ一覧画面へリダイレクト
5. 失敗時：エラーメッセージを表示

**送信データ形式**:
```json
{
  "operations_feature_flag": {
    "name": "flag_name",
    "description": "description",
    "active": true,
    "strategies_attributes": [
      {
        "id": 1,
        "name": "default",
        "parameters": {},
        "_destroy": false,
        "scopes_attributes": [
          { "id": 1, "environment_scope": "*" }
        ]
      }
    ]
  }
}
```

**遷移先**: フィーチャーフラグ一覧画面 または 同一画面（エラー時）

### 3-戦略追加

**トリガー**: 「Add strategy」ボタン押下

**処理フロー**: 新規作成画面と同様

**遷移先**: 画面内更新

### 4-戦略削除

**トリガー**: 戦略の削除ボタン押下

**処理フロー**:
1. 既存戦略（IDあり）の場合：`shouldBeDestroyed: true`をセット
2. 新規追加戦略（IDなし）の場合：配列から削除

**遷移先**: 画面内更新

### 5-キャンセル

**トリガー**: 「Cancel」ボタン押下

**処理フロー**: フィーチャーフラグ一覧画面へ遷移

**遷移先**: `/:namespace/:project/-/feature_flags`

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | operations_feature_flags | SELECT | フィーチャーフラグ情報の取得 |
| 画面表示 | operations_feature_flags_strategies | SELECT | 戦略情報の取得 |
| 有効/無効切り替え | operations_feature_flags | UPDATE | activeカラムの更新 |
| 変更保存 | operations_feature_flags | UPDATE | 名前、説明等の更新 |
| 変更保存 | operations_feature_flags_strategies | INSERT/UPDATE/DELETE | 戦略の追加・更新・削除 |
| 変更保存 | operations_scopes | INSERT/UPDATE/DELETE | スコープの追加・更新・削除 |

### テーブル別更新項目詳細

#### operations_feature_flags

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, iid, name, description, active, version, strategies | iid = パスパラメータ | 初期表示時 |
| UPDATE | name, description, active | フォーム入力値 | 保存時 |

#### operations_feature_flags_strategies

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | feature_flag_id, name, parameters, user_list_id | 新規戦略 | _destroyなし、IDなし |
| UPDATE | name, parameters, user_list_id | 既存戦略 | _destroyなし、IDあり |
| DELETE | - | id = 戦略ID | _destroy: true |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | エラー | バリデーションエラーメッセージ（配列） | 保存失敗時 |
| MSG-002 | ラベル | Feature flag status | トグルのラベル |
| MSG-003 | ボタン | Save changes | 保存ボタン |

## 例外処理

| 例外 | 原因 | 対処 |
|------|------|------|
| 404 Not Found | 指定されたフィーチャーフラグが存在しない | エラーページ表示 |
| 403 Forbidden | update_feature_flag権限がない | アクセス拒否画面へリダイレクト |
| 422 Unprocessable Entity | バリデーションエラー | エラーメッセージを表示 |

## 備考

- 画面表示時に`fetchFeatureFlag`でデータを取得する
- ローディング中は`GlLoadingIcon`を表示
- 戦略の削除は論理削除（`_destroy: true`）として送信され、サーバー側で物理削除される
- EE版では「FeatureFlagActions」コンポーネントが追加で表示される可能性がある

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | feature_flag.rb | `app/models/operations/feature_flag.rb` | モデル定義、accepts_nested_attributes |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | feature_flags_controller.rb | `app/controllers/projects/feature_flags_controller.rb` | edit, update, showアクション |
| 2-2 | edit.html.haml | `app/views/projects/feature_flags/edit.html.haml` | Vueマウントポイント |

**主要処理フロー**:
1. **8行目**: `authorize_update_feature_flag!`で更新権限チェック
2. **11行目**: `before_action :feature_flag`で対象フラグを取得
3. **59行目**: `edit`アクションは空（Vue側でデータ取得）
4. **61-73行目**: `update`アクションで`FeatureFlags::UpdateService`を呼び出し

#### Step 3: フロントエンド（Vue.js）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit_feature_flag.vue | `app/assets/javascripts/feature_flags/components/edit_feature_flag.vue` | 編集画面メインコンポーネント |
| 3-2 | form.vue | `app/assets/javascripts/feature_flags/components/form.vue` | フォームコンポーネント（共通） |

**主要処理フロー**:
- **34-36行目** (edit_feature_flag.vue): `created`で`fetchFeatureFlag`を実行
- **53-62行目** (edit_feature_flag.vue): ステータストグルで`toggleActive`を呼び出し
- **79行目** (edit_feature_flag.vue): フォーム送信で`updateFeatureFlag`を呼び出し

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

```
[ブラウザ]
    │
    ├─ GET /feature_flags/:iid/edit (HTML)
    │      └─ FeatureFlagsController#edit
    │             └─ HTML response (Vue mount point)
    │
    ├─ GET /feature_flags/:iid.json (データ取得)
    │      └─ FeatureFlagsController#show
    │             └─ FeatureFlagSerializer
    │
    └─ PUT /feature_flags/:iid.json (更新)
           └─ FeatureFlagsController#update
                  └─ FeatureFlags::UpdateService#execute
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| feature_flag.rb | `app/models/operations/feature_flag.rb` | モデル | FeatureFlagモデル定義 |
| feature_flags_controller.rb | `app/controllers/projects/feature_flags_controller.rb` | コントローラー | HTTPリクエスト処理 |
| edit.html.haml | `app/views/projects/feature_flags/edit.html.haml` | ビュー | HTMLテンプレート |
| edit_feature_flag.vue | `app/assets/javascripts/feature_flags/components/edit_feature_flag.vue` | Vueコンポーネント | 編集画面 |
| form.vue | `app/assets/javascripts/feature_flags/components/form.vue` | Vueコンポーネント | フォーム（共通） |
| update_service.rb | `app/services/feature_flags/update_service.rb` | サービス | 更新ビジネスロジック |
