# 画面設計書 82-フィーチャーフラグ一覧

## 概要

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

### 本画面の処理概要

フィーチャーフラグ一覧画面は、プロジェクトに登録されているフィーチャーフラグを一覧表示し、管理するための画面である。フィーチャーフラグは、機能の有効/無効を動的に切り替えることができる機能であり、段階的なリリースやA/Bテストなどに活用される。

**業務上の目的・背景**：ソフトウェア開発において、新機能を段階的にリリースしたい、特定のユーザーグループにのみ機能を提供したい、本番環境で問題が発生した際に即座に機能を無効化したいといったニーズがある。フィーチャーフラグはこれらの要件を満たすための仕組みであり、本画面はプロジェクト内のフィーチャーフラグを俯瞰し、迅速に状態を把握・変更できる場を提供する。Unleashプロトコル互換のクライアントライブラリと連携することで、アプリケーションコードから動的にフラグの状態を参照できる。

**画面へのアクセス方法**：プロジェクトのサイドメニューから「Deploy」>「Feature flags」を選択、またはURL直接アクセス。URLパターンは `/:namespace/:project/-/feature_flags` となる。

**主要な操作・処理内容**：
1. フィーチャーフラグ一覧の表示（ID、ステータス、名前、環境スコープ）
2. フィーチャーフラグの有効/無効切り替え（トグル操作）
3. フィーチャーフラグの新規作成画面への遷移
4. フィーチャーフラグの編集画面への遷移
5. フィーチャーフラグの削除
6. ユーザーリスト管理画面への遷移
7. Unleash API設定の確認（Configure モーダル）
8. Instance IDのローテーション

**画面遷移**：
- 遷移元：プロジェクトサイドメニュー、プロジェクト詳細画面
- 遷移先：フィーチャーフラグ新規作成画面、フィーチャーフラグ編集画面、ユーザーリスト管理画面

**権限による表示制御**：
- `read_feature_flag`権限がない場合はアクセス不可
- `admin_feature_flag`権限を持つユーザーのみ「Configure」ボタンが表示される
- `create_feature_flag`権限を持つユーザーのみ「New feature flag」ボタンが表示される
- `admin_feature_flags_client`権限を持つユーザーのみInstance IDのローテーションが可能
- `admin_feature_flags_user_lists`権限を持つユーザーのみ「View user lists」ボタンが表示される
- 各フィーチャーフラグの`update_path`がある場合のみトグル操作が可能
- 各フィーチャーフラグの`destroy_path`がある場合のみ削除ボタンが表示される

## 関連機能

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

## 画面種別

一覧

## URL/ルーティング

```
GET /:namespace/:project/-/feature_flags
GET /:namespace/:project/-/feature_flags.json (APIエンドポイント)
```

**ルート定義** (`config/routes/project.rb`):
```ruby
resources :feature_flags, param: :iid
```

## 入出力項目

### フィルタ項目

| 項目名 | データ型 | 必須 | 説明 |
|--------|----------|------|------|
| scope | String | - | フィルタ条件（画面上の操作ではなく、URLパラメータで指定可能） |
| page | Integer | - | ページ番号（デフォルト: 1） |

## 表示項目

### フィーチャーフラグテーブル

| 項目名 | データ型 | 説明 | ソース |
|--------|----------|------|--------|
| ID | String | フィーチャーフラグのIID（^付き表記） | feature_flag.iid |
| Status | Boolean | フィーチャーフラグの有効/無効状態 | feature_flag.active |
| Feature flag | String | フィーチャーフラグ名 | feature_flag.name |
| Description | String | フィーチャーフラグの説明（ツールチップ表示） | feature_flag.description |
| Environment Specs | Array<Strategy> | 適用戦略と環境スコープの一覧 | feature_flag.strategies |
| Actions | - | 編集・削除ボタン | edit_path, destroy_path |

### ヘッダー情報

| 項目名 | データ型 | 説明 | ソース |
|--------|----------|------|--------|
| タイトル | String | "Feature flags" | 固定値 |
| カウントバッジ | String | フィーチャーフラグ数（制限がある場合は "現在数/上限数"） | count, featureFlagsLimit |

### ページネーション情報

| 項目名 | データ型 | 説明 | ソース |
|--------|----------|------|--------|
| total | Integer | 総件数 | pageInfo.total |
| perPage | Integer | 1ページあたりの表示件数（30件） | pageInfo.perPage |
| currentPage | Integer | 現在のページ | pageInfo.currentPage |

## イベント仕様

### 1-フィーチャーフラグ有効/無効切り替え

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

**前提条件**:
- フィーチャーフラグに`update_path`が設定されている

**処理フロー**:
1. Vuexアクション`toggleFeatureFlag`を実行
2. PUTリクエストを`update_path`に送信（active値を反転）
3. 成功時：一覧を更新
4. 失敗時：アラート表示、元の状態に戻す

**遷移先**: 画面更新（同一画面）

### 2-フィーチャーフラグ新規作成

**トリガー**: 「New feature flag」ボタン押下

**前提条件**:
- `create_feature_flag`権限を持つ
- フィーチャーフラグ上限に達していない

**処理フロー**: 新規作成画面へ遷移

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

### 3-フィーチャーフラグ編集

**トリガー**: 編集ボタン（鉛筆アイコン）押下

**前提条件**:
- フィーチャーフラグに`edit_path`が設定されている

**処理フロー**: 編集画面へ遷移

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

### 4-フィーチャーフラグ削除

**トリガー**: 削除ボタン押下 → 確認モーダルで「Delete feature flag」押下

**前提条件**:
- フィーチャーフラグに`destroy_path`が設定されている
- 全てのスコープで`can_update`がtrue

**処理フロー**:
1. 削除確認モーダルを表示
2. 確認後、DELETEリクエストを`destroy_path`に送信（フォームPOST）
3. 成功時：一覧から削除され、リダイレクト
4. 失敗時：アラート表示

**遷移先**: 画面リロード（同一画面）

### 5-Configure設定表示

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

**前提条件**:
- `admin_feature_flag`権限を持つ

**処理フロー**:
1. ConfigureFeatureFlagsModalを表示
2. Unleash API URLとInstance IDを表示
3. Instance IDローテーションボタンの提供

**遷移先**: モーダル表示（画面遷移なし）

### 6-Instance IDローテーション

**トリガー**: Configureモーダル内の「Generate new instance ID」ボタン押下

**前提条件**:
- `admin_feature_flags_client`権限を持つ

**処理フロー**:
1. Vuexアクション`rotateInstanceId`を実行
2. POSTリクエストを`rotateEndpoint`に送信
3. 成功時：新しいInstance IDを表示
4. 失敗時：エラー表示

**遷移先**: モーダル内更新

### 7-ユーザーリスト画面への遷移

**トリガー**: 「View user lists」ボタン押下

**前提条件**:
- `admin_feature_flags_user_lists`権限を持つ

**処理フロー**: ユーザーリスト画面へ遷移

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

### 8-ページネーション

**トリガー**: ページ番号クリック

**処理フロー**:
1. URLパラメータを更新（page=N）
2. historyPushStateでURLを変更
3. `fetchFeatureFlags`を実行して新しいページのデータを取得

**遷移先**: 同一画面（URLパラメータ変更）

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | operations_feature_flags | SELECT | フィーチャーフラグ一覧の取得 |
| 画面表示 | operations_feature_flags_strategies | SELECT | 戦略情報の取得 |
| 画面表示 | operations_scopes | SELECT | スコープ情報の取得 |
| 有効/無効切り替え | operations_feature_flags | UPDATE | active列の更新 |
| 削除 | operations_feature_flags | DELETE | フィーチャーフラグの削除 |
| 削除 | operations_feature_flags_strategies | DELETE | 関連戦略の削除 |
| Instance IDローテーション | operations_feature_flags_clients | UPDATE | tokenの更新 |

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

#### operations_feature_flags

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, iid, project_id, name, description, active, version | project_id = プロジェクトID | ページネーション付き（30件/ページ） |
| UPDATE | active | true/false | トグル操作時 |
| DELETE | - | id = 対象フィーチャーフラグID | 削除操作時 |

#### operations_feature_flags_strategies

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, feature_flag_id, name, parameters, user_list_id | feature_flag_idで結合 | strategies関連 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 警告 | You've reached your feature flag limit (N). To add more, delete at least one feature flag, or upgrade to a higher tier. | 上限到達時 |
| MSG-002 | 成功 | Feature flag was successfully removed. | 削除成功時 |
| MSG-003 | エラー | Feature flag was not removed. | 削除失敗時 |
| MSG-004 | エラー | There was an error fetching the feature flags. | 一覧取得エラー時 |
| MSG-005 | 情報 | Loading feature flags | ローディング中 |
| MSG-006 | 情報 | Get started with feature flags | 空状態時のタイトル |
| MSG-007 | 情報 | Feature flags allow you to configure your code into different flavors by dynamically toggling certain functionality. | 空状態時の説明 |
| MSG-008 | 確認 | Delete {name}? | 削除確認モーダルタイトル |
| MSG-009 | 確認 | Feature flag {name} will be removed. Are you sure? | 削除確認メッセージ |

## 例外処理

| 例外 | 原因 | 対処 |
|------|------|------|
| 403 Forbidden | read_feature_flag権限がない | アクセス拒否画面へリダイレクト |
| API Error | 一覧取得失敗 | エラーメッセージ表示、リトライ促進 |
| Limit Exceeded | フィーチャーフラグ上限到達 | 警告アラート表示、新規作成ボタン無効化 |

## 備考

- フィーチャーフラグはUnleashプロトコルに準拠しており、各種クライアントライブラリから利用可能
- 一覧は10秒間隔でポーリング更新される（`Gitlab::PollingInterval.set_header`）
- プロジェクトごとにフィーチャーフラグの上限が設定されている場合がある（`project.actual_limits.project_feature_flags`）
- フィーチャーフラグ名は2-63文字、小文字・数字・アンダースコア・ハイフンのみ使用可能

---

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

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

### 推奨読解順序

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

まず、FeatureFlagモデルの構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | feature_flag.rb | `app/models/operations/feature_flag.rb` | モデル定義、バリデーション、アソシエーションを確認 |

**読解のコツ**: `Operations::FeatureFlag`は`operations_feature_flags`テーブルに対応。`strategies`関連で戦略パターンを管理している。`version`は`new_version_flag`のみが有効（旧バージョンは廃止済み）。

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

コントローラーとビューテンプレートの処理起点を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | feature_flags_controller.rb | `app/controllers/projects/feature_flags_controller.rb` | indexアクションの実装、認可処理、JSONレスポンス |
| 2-2 | index.html.haml | `app/views/projects/feature_flags/index.html.haml` | Vueマウントポイントとdata属性の受け渡し |

**主要処理フロー**:
1. **6-9行目**: before_action認可チェック
2. **16-31行目**: `index`アクション - FeatureFlagsFinder経由で取得、ページネーション（30件/ページ）
3. **26行目**: `Gitlab::PollingInterval.set_header`で10秒ポーリング設定

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | feature_flags.vue | `app/assets/javascripts/feature_flags/components/feature_flags.vue` | メインコンポーネント、Vuex連携 |
| 3-2 | feature_flags_table.vue | `app/assets/javascripts/feature_flags/components/feature_flags_table.vue` | テーブル表示、トグル操作、削除処理 |
| 3-3 | configure_feature_flags_modal.vue | `app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue` | Unleash API設定モーダル |

**主要処理フロー**:
- **56-67行目** (feature_flags.vue): Vuex mapState でfeatureFlags, count, pageInfo等を取得
- **122-125行目** (feature_flags.vue): created時にsetFeatureFlagsOptions、fetchFeatureFlagsを実行
- **111-116行目** (feature_flags_table.vue): toggleFeatureFlagでactive状態を反転してemit

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

```
[ブラウザ]
    │
    ├─ GET /feature_flags (HTML)
    │      └─ FeatureFlagsController#index
    │             └─ HTML response (Vue mount point)
    │
    ├─ GET /feature_flags.json (API)
    │      └─ FeatureFlagsController#index
    │             └─ FeatureFlagsFinder#execute
    │                    └─ FeatureFlag.page(params[:page]).per(30)
    │                           └─ JSON response (FeatureFlagSerializer)
    │
    ├─ PUT /feature_flags/:iid.json (toggle)
    │      └─ FeatureFlagsController#update
    │             └─ FeatureFlags::UpdateService#execute
    │
    └─ DELETE /feature_flags/:iid (delete)
           └─ FeatureFlagsController#destroy
                  └─ FeatureFlags::DestroyService#execute
```

### データフロー図

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

ブラウザリクエスト    ───▶  FeatureFlagsController#index  ───▶  HTML (Vueマウントポイント)
                                   │
                                   ▼
Vue.js created          ───▶  Vuex fetchFeatureFlags      ───▶  API Request (.json)
                                   │
                                   ▼
                           FeatureFlagsController#index (json)
                                   │
                                   ▼
                           FeatureFlagsFinder
                                   │
                                   ▼
                           Operations::FeatureFlag
                                   │
                                   ▼
                           PostgreSQL
                           (operations_feature_flags)
                                   │
                                   ▼
                           FeatureFlagSerializer  ───▶  JSON Response  ───▶  Vuex State
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| feature_flag.rb | `app/models/operations/feature_flag.rb` | モデル | FeatureFlagモデル定義 |
| feature_flags_controller.rb | `app/controllers/projects/feature_flags_controller.rb` | コントローラー | HTTPリクエスト処理 |
| index.html.haml | `app/views/projects/feature_flags/index.html.haml` | ビュー | HTMLテンプレート |
| feature_flags.vue | `app/assets/javascripts/feature_flags/components/feature_flags.vue` | Vueコンポーネント | メイン一覧コンポーネント |
| feature_flags_table.vue | `app/assets/javascripts/feature_flags/components/feature_flags_table.vue` | Vueコンポーネント | テーブル表示 |
| empty_state.vue | `app/assets/javascripts/feature_flags/components/empty_state.vue` | Vueコンポーネント | 空状態・エラー表示 |
| configure_feature_flags_modal.vue | `app/assets/javascripts/feature_flags/components/configure_feature_flags_modal.vue` | Vueコンポーネント | 設定モーダル |
| feature_flags_finder.rb | `app/finders/feature_flags_finder.rb` | ファインダー | 検索・フィルタリング |
| feature_flag_serializer.rb | `app/serializers/feature_flag_serializer.rb` | シリアライザー | JSON変換 |
| create_service.rb | `app/services/feature_flags/create_service.rb` | サービス | 作成処理 |
| update_service.rb | `app/services/feature_flags/update_service.rb` | サービス | 更新処理 |
| destroy_service.rb | `app/services/feature_flags/destroy_service.rb` | サービス | 削除処理 |
