# 機能設計書 10-グループ編集

## 概要

本ドキュメントは、GitLabにおけるグループ編集機能の詳細設計を記述する。この機能は、既存のグループの名前、パス、説明、可視性レベル、各種設定を変更するための機能である。

### 本機能の処理概要

グループ編集機能は、グループの属性や設定を更新し、組織構造の変化やポリシーの変更に対応するための機能を提供する。

**業務上の目的・背景**：組織名の変更、セキュリティポリシーの更新、機能設定の調整などのために、グループの設定を変更する必要がある。グループ編集機能により、管理者はグループの属性を柔軟に管理できる。

**機能の利用シーン**：
- グループ名やパスを変更する時
- 可視性レベルを調整する時
- 共有ランナー設定を変更する時
- CRM設定を更新する時
- 二要素認証ポリシーを変更する時

**主要な処理内容**：
1. 管理者権限の確認
2. パラメータの検証
3. Container Registry画像存在チェック（パス変更時）
4. 可視性レベルの検証
5. 共有ランナー設定の更新
6. CRM設定の更新
7. グループ属性の更新
8. 後処理（可視性変更時のTodo削除等）

**関連システム・外部連携**：
- Container Registry（パス変更制限）
- 共有ランナー管理

**権限による制御**：
- グループ管理者（admin_group）権限が必要
- 一部設定は追加の権限が必要（emails_enabled, max_artifacts_size等）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | グループ設定 | 主画面 | グループ情報編集 |

## 機能種別

CRUD操作（Update）/ バリデーション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | String | No | グループ名 | 文字数制限 |
| path | String | No | URLパス | 一意性、NPMパッケージ制限 |
| description | String | No | 説明 | - |
| visibility_level | Integer | No | 可視性レベル | 許可されたレベル |
| avatar | File | No | アバター画像 | 画像フォーマット |
| shared_runners_setting | String | No | 共有ランナー設定 | enabled/disabled_and_overridable/disabled_and_unoverridable |
| crm_enabled | Boolean | No | CRM有効化 | - |
| emails_enabled | Boolean | No | メール有効化 | set_emails_disabled権限必要 |
| max_artifacts_size | Integer | No | 最大アーティファクトサイズ | update_max_artifacts_size権限必要 |

### 入力データソース

- 画面入力（Web UI - Group Settings画面）
- API リクエスト（REST API / GraphQL）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| result | Boolean | 処理結果（true: 成功、false: 失敗） |
| error | String | エラーメッセージ（失敗時） |

### 出力先

- データベース（namespaces, routes, namespace_settings等）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受付（GroupsController#update）
   └─ パラメータ確認

2. 権限チェック（authorize_admin_group!）
   └─ admin_group権限確認

3. サービス呼び出し（Groups::UpdateService#execute）

4. 事前チェック
   ├─ parent_id削除（変更不可）
   ├─ 不許可パラメータ削除
   ├─ Container Registry画像チェック（パス変更時）
   ├─ 可視性レベル検証
   └─ share_with_group_lock検証

5. パス変更チェック（NPMパッケージ）
   └─ ルートグループのパス変更制限

6. 設定更新
   ├─ 共有ランナー設定
   ├─ namespace_settings
   └─ CRM設定

7. グループ属性更新
   └─ group.assign_attributes & save

8. 後処理
   ├─ 可視性変更時のTodo削除
   └─ 二要素認証設定変更時の処理

9. レスポンス返却
   └─ 編集画面へリダイレクト
```

### フローチャート

```mermaid
flowchart TD
    A[開始: update リクエスト] --> B{admin_group権限?}
    B -->|No| C[エラー: 権限なし]
    B -->|Yes| D[UpdateService#execute]
    D --> E[reject_parent_id!]
    E --> F[remove_unallowed_params]
    F --> G{パス変更?}
    G -->|Yes| H{CR画像あり?}
    H -->|Yes| I[エラー: CR画像存在]
    H -->|No| J{NPMパッケージあり?}
    J -->|Yes| K[エラー: NPMパッケージ存在]
    G -->|No| L[可視性レベル検証]
    J -->|No| L
    L --> M{可視性変更有効?}
    M -->|No| N[エラー: 可視性不正]
    M -->|Yes| O{share_with_group_lock変更有効?}
    O -->|No| P[エラー: ロック変更不可]
    O -->|Yes| Q[共有ランナー設定更新]
    Q --> R[handle_changes]
    R --> S[group.assign_attributes]
    S --> T[group.save]
    T --> U{保存成功?}
    U -->|No| V[エラー返却]
    U -->|Yes| W[after_update]
    W --> X{可視性がprivateに?}
    X -->|Yes| Y[Todo削除スケジュール]
    X -->|No| Z[成功レスポンス]
    Y --> Z

    C --> AA[終了]
    I --> AA
    K --> AA
    N --> AA
    P --> AA
    V --> AA
    Z --> AA
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 管理者権限必須 | グループ編集には管理者権限が必要 | 常時 |
| BR-002 | parent_id変更不可 | 親グループは変更できない | 常時 |
| BR-003 | CR画像制限 | Container Registry画像があるとパス変更不可 | パス変更時 |
| BR-004 | NPMパッケージ制限 | ルートグループでNPMパッケージがあるとパス変更不可 | ルートグループのパス変更時 |
| BR-005 | share_with_group_lock継承 | 親がロックしている場合、子は解除不可 | ロック解除時 |

### 計算ロジック

可視性レベル検証：
```ruby
# 変更が有効かチェック
def valid_visibility_level_change?(group, visibility)
  return true if visibility.nil?

  # 子孫の可視性を超えないこと
  # アプリケーション設定に従うこと
  group.visibility_level_allowed?(visibility)
end
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| グループ更新 | namespaces | UPDATE | グループ属性更新 |
| ルート更新 | routes | UPDATE | パス変更時 |
| リダイレクト追加 | redirect_routes | INSERT | パス変更時 |
| 設定更新 | namespace_settings | UPDATE | 設定変更 |
| CRM設定更新 | group_crm_settings | UPDATE/INSERT | CRM設定変更 |

### テーブル別操作詳細

#### namespaces

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | name | params[:name] | グループ名 |
| UPDATE | path | params[:path] | URLパス |
| UPDATE | description | params[:description] | 説明 |
| UPDATE | visibility_level | params[:visibility_level] | 可視性レベル |
| UPDATE | shared_runners_enabled | 計算値 | 共有ランナー設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| E001 | 権限エラー | 管理者権限なし | 権限を取得 |
| E002 | CR制限 | パス変更時にCR画像存在 | CR画像を削除後に変更 |
| E003 | NPM制限 | パス変更時にNPMパッケージ存在 | パッケージ削除後に変更 |
| E004 | 可視性エラー | 許可されない可視性変更 | 許可されたレベルに変更 |
| E005 | ロックエラー | 親がロック時の解除試行 | 親グループでロック解除 |
| E006 | CRM制限 | 連絡先がある状態でソース変更 | 連絡先削除後に変更 |

### リトライ仕様

- 更新失敗時はロールバック
- パス変更失敗はGitlab::UpdatePathErrorで捕捉

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

- グループ属性更新は単一トランザクション
- 後処理（Todo削除等）は非同期ワーカー

## パフォーマンス要件

- 更新処理は即座に完了
- 低緊急度（urgency: :low）として設定

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

- 管理者権限チェック必須
- 特定パラメータは追加権限が必要
- 可視性変更はセキュリティに影響

## 備考

- 可視性をprivateに変更すると関連Todoが削除される
- 二要素認証設定の変更は子グループのメンバーにも影響
- パス変更時は旧パスからのリダイレクトが設定される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | groups_controller.rb | `app/controllers/groups_controller.rb` | updateアクションの実装 |

**主要処理フロー**:
1. **156-169行目**: updateアクションの定義
2. **23行目**: authorize_admin_group!の権限チェック

#### Step 2: ビジネスロジック層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | update_service.rb | `app/services/groups/update_service.rb` | 更新の中心ロジック |

**主要処理フロー**:
- **11-44行目**: executeメソッドのメイン処理
- **48-65行目**: valid_path_change?でパス変更検証
- **72-84行目**: renaming_group_with_container_registry_images?でCR検証
- **86-93行目**: after_updateで後処理
- **130-133行目**: handle_changesで設定更新
- **166-178行目**: valid_share_with_group_lock_change?でロック検証
- **180-189行目**: update_shared_runnersで共有ランナー更新

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

```
GroupsController#update
    │
    ├─ authorize_admin_group!
    │
    └─ Groups::UpdateService#execute
           │
           ├─ reject_parent_id!
           │
           ├─ remove_unallowed_params
           │      ├─ emails_enabled (権限チェック)
           │      ├─ max_artifacts_size (権限チェック)
           │      └─ default_branch_protection (権限チェック)
           │
           ├─ renaming_group_with_container_registry_images?
           │
           ├─ valid_visibility_level_change?
           │
           ├─ valid_share_with_group_lock_change?
           │
           ├─ valid_path_change?
           │      └─ NPMパッケージ検証
           │
           ├─ update_shared_runners
           │      └─ Groups::UpdateSharedRunnersService
           │
           ├─ handle_changes
           │      ├─ handle_settings_update
           │      │      └─ NamespaceSettings::AssignAttributesService
           │      └─ handle_crm_settings_update
           │
           ├─ group.assign_attributes
           │
           ├─ group.save
           │
           └─ after_update
                  ├─ [可視性private時]
                  │      └─ TodosDestroyer::GroupPrivateWorker
                  └─ update_two_factor_requirement_for_subgroups
```

### データフロー図

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

グループ設定           GroupsController#update
フォーム入力                 │
         │                  │
         ▼                  ▼
              ┌───────────────────────────────┐
              │ 権限チェック                   │
              │ authorize_admin_group!        │
              └─────────────┬─────────────────┘
                            │
                            ▼
              ┌───────────────────────────────┐
              │ Groups::UpdateService#execute │
              │                               │
              │ ├─ パラメータ検証             │
              │ ├─ パス変更検証               │
              │ ├─ 可視性検証                 │
              │ └─ 設定更新                   │
              └─────────────┬─────────────────┘
                            │
                            ▼
              ┌───────────────────────────────┐      ┌─────────────────────┐
              │ group.save                    │ ───▶ │ namespaces          │
              │                               │      │ routes              │
              │                               │      │ namespace_settings  │
              └─────────────┬─────────────────┘      └─────────────────────┘
                            │
                            ▼
              ┌───────────────────────────────┐
              │ after_update                  │
              │                               │
              │ [可視性private時]             │
              │ └─ TodosDestroyer (非同期)    │
              └─────────────┬─────────────────┘
                            │
                            ▼
              ┌───────────────────────────────┐
              │ リダイレクト                   │
              │ → グループ設定ページ          │
              └───────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| groups_controller.rb | `app/controllers/groups_controller.rb` | ソース | コントローラー |
| update_service.rb | `app/services/groups/update_service.rb` | ソース | 更新ビジネスロジック |
| update_shared_runners_service.rb | `app/services/groups/update_shared_runners_service.rb` | ソース | 共有ランナー更新 |
| assign_attributes_service.rb | `app/services/namespace_settings/assign_attributes_service.rb` | ソース | 名前空間設定更新 |
| group.rb | `app/models/group.rb` | ソース | グループモデル |
| namespace_setting.rb | `app/models/namespace_setting.rb` | ソース | 名前空間設定モデル |
| group_private_worker.rb | `app/workers/todos_destroyer/group_private_worker.rb` | ソース | Todo削除ワーカー |
