# 機能設計書 12-グループ移行

## 概要

本ドキュメントは、GitLabにおけるグループ移行機能の設計仕様を定義する。グループを別の親グループに移動する機能について、処理フロー、入出力仕様、ビジネスルールを記載する。

### 本機能の処理概要

グループ移行機能は、既存のグループを別の親グループ配下に移動させる機能である。移行により、グループのパス、可視性レベル、共有Runner設定などが移行先の親グループの設定に合わせて調整される。

**業務上の目的・背景**：組織再編やプロジェクトの再配置により、グループの階層構造を変更する必要がある場合に使用する。これにより、組織の実態に合わせたグループ構成を維持し、適切なアクセス権限管理を実現する。

**機能の利用シーン**：
- 組織改編により部署配下のチームを別の部署に移動する場合
- プロジェクトの所有者変更に伴いグループを移動する場合
- グループ階層の整理・最適化を行う場合
- ルートグループをサブグループに変更する場合（またはその逆）

**主要な処理内容**：
1. 移行権限の確認（元グループの変更権限、移行先での作成権限）
2. 移行可否の検証（パス重複、コンテナレジストリ、NPMパッケージ等）
3. グループ属性の更新（親グループ、可視性レベル、2FA設定）
4. インテグレーション設定の更新
5. プロジェクト権限の再計算
6. ラベルの移行
7. イベントの発行

**関連システム・外部連携**：
- CRMオブジェクトの移行（連絡先、組織）
- インテグレーションの伝播（PropagateIntegrationWorker）
- CI/CDペンディングビルドの更新

**権限による制御**：
- 元グループに対する変更権限（change_group）が必要
- 移行先グループでのサブグループ作成権限（create_subgroup）が必要
- ルートグループへの移行時はグループ作成権限（create_group）が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 143 | グループ編集 | 主画面 | グループ移行オプションの操作 |
| 191 | グループ編集（組織配下） | 参照画面 | 組織配下グループの移行 |

## 機能種別

CRUD操作（Update）/ データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 移行対象グループのID | 存在するグループであること |
| new_parent_group_id | Integer | No | 移行先親グループのID（nil:ルートグループ化） | 存在するグループであること |

### 入力データソース

- 画面入力：移行先グループ選択
- URLパラメータ：グループID

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| success | Boolean | 移行成功/失敗 |
| error | String | エラーメッセージ（失敗時） |

### 出力先

- 画面表示：移行完了/エラーメッセージ
- 監査ログ：移行操作の記録
- イベントストア：GroupTransferedEventの発行

## 処理フロー

### 処理シーケンス

```
1. 移行リクエスト受付
   └─ GroupsController#transferが呼び出される

2. 権限チェック
   └─ authorize_change_group!により変更権限を確認

3. 移行先グループの取得
   └─ new_parent_group_idからGroupを取得

4. 移行サービス実行
   └─ Groups::TransferService#execute
      └─ ensure_allowed_transfer: 移行可否チェック
      └─ proceed_to_transfer: 実際の移行処理

5. 移行処理詳細
   ├─ update_group_attributes: グループ属性更新
   ├─ ensure_ownership: オーナー確保
   ├─ update_integrations: インテグレーション更新
   ├─ update_crm_objects: CRMオブジェクト移行
   ├─ publish_event: イベント発行
   ├─ transfer_labels: ラベル移行
   ├─ refresh_project_authorizations: 権限再計算
   └─ propagate_integrations: インテグレーション伝播

6. 結果返却
   └─ 成功/失敗に応じてリダイレクト
```

### フローチャート

```mermaid
flowchart TD
    A[移行リクエスト] --> B{権限チェック}
    B -->|権限なし| C[エラー: 権限不足]
    B -->|権限あり| D{移行可否チェック}
    D -->|既にルート| E[エラー: 既にルート]
    D -->|同じ親| F[エラー: 同じ親]
    D -->|パス重複| G[エラー: パス重複]
    D -->|コンテナ画像あり| H[エラー: コンテナ制限]
    D -->|サブグループ宛| I[エラー: サブグループ宛不可]
    D -->|OK| J[移行処理開始]
    J --> K[可視性レベル調整]
    K --> L[2FA設定更新]
    L --> M[グループ属性保存]
    M --> N[オーナー確保]
    N --> O[インテグレーション更新]
    O --> P[イベント発行]
    P --> Q[ラベル移行]
    Q --> R[権限再計算]
    R --> S[移行完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 権限要件 | 元グループの変更権限と移行先での作成権限が必要 | 常時 |
| BR-02 | ルートグループ制限 | 既にルートグループの場合は移行不可 | 親なしへの移行時 |
| BR-03 | 同一親制限 | 現在と同じ親グループへは移行不可 | 常時 |
| BR-04 | パス重複制限 | 移行先に同名パスが存在する場合は移行不可 | 常時 |
| BR-05 | コンテナレジストリ制限 | コンテナイメージを含むグループは移行不可 | 常時 |
| BR-06 | サブグループ移行制限 | 自身のサブグループへの移行は不可 | 常時 |
| BR-07 | NPMパッケージ制限 | ルートグループスコープのNPMパッケージがある場合は移行不可 | 異なるルート祖先への移行時 |
| BR-08 | CRM権限要件 | CRMデータがある場合、移行先でのCRM管理権限が必要 | CRMデータ存在時 |
| BR-09 | 可視性レベル継承 | 移行先の可視性レベルより高い場合は自動的に引き下げ | 移行先の可視性が低い場合 |
| BR-10 | 2FA設定継承 | 移行先で2FAが許可されていない場合、設定を無効化 | 移行先で2FA非許可時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 親グループ変更 | namespaces | UPDATE | parent_idの更新 |
| 可視性更新 | namespaces | UPDATE | 可視性レベルの更新（必要時） |
| 子グループ更新 | namespaces | UPDATE | 子グループの可視性更新 |
| プロジェクト更新 | projects | UPDATE | プロジェクトの可視性更新 |
| インテグレーション削除 | integrations | DELETE | デフォルト設定の削除 |
| インテグレーション作成 | integrations | INSERT | 新しいデフォルト設定の作成 |
| CRM移行 | customer_relations_contacts | UPDATE | グループIDの更新 |

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

#### namespaces

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | parent_id | 新しい親グループID（または null） | - |
| UPDATE | visibility_level | 親グループの可視性（必要時） | - |
| UPDATE | require_two_factor_authentication | false（親が許可しない場合） | - |

#### integrations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | group_id | 対象グループID | デフォルト設定の削除 |
| INSERT | group_id | 対象グループID | 新規デフォルト設定作成 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| group_is_already_root | 移行不可 | 既にルートグループ | エラーメッセージ表示 |
| same_parent_as_current | 移行不可 | 同じ親グループ | エラーメッセージ表示 |
| namespace_with_same_path | 移行不可 | パス重複 | エラーメッセージ表示 |
| group_contains_images | 移行不可 | コンテナイメージ存在 | イメージ削除後に再試行 |
| cannot_transfer_to_subgroup | 移行不可 | サブグループ宛 | 別の移行先を選択 |
| group_contains_namespaced_npm_packages | 移行不可 | NPMパッケージ存在 | パッケージ対応後に再試行 |
| no_permissions_to_migrate_crm | 移行不可 | CRM権限不足 | 権限取得後に再試行 |
| invalid_policies | 権限不足 | 必要権限がない | 権限確認 |

### リトライ仕様

- トランザクション内でエラーが発生した場合、ロールバック
- リトライは手動で再実行

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

- グループ属性の更新、オーナー確保、インテグレーション更新、CRM移行はトランザクション内で実行
- ラベル移行、権限再計算、インテグレーション伝播はトランザクション外で実行

## パフォーマンス要件

- 大量のプロジェクト・サブグループを含むグループの移行は処理時間がかかる
- 権限再計算はAuthorizedProjectUpdate::ProjectAccessChangedServiceで実行

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

- 移行権限の厳格なチェック
- 可視性レベルの自動調整による情報漏洩防止
- CRM権限チェックによるデータ保護

## 備考

- 移行後、元のURLからは自動的にリダイレクトされる（Routeの仕組み）
- サブスクリプションを持つグループは移行不可

---

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

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

### 推奨読解順序

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

グループの階層構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | group.rb | `app/models/group.rb` | parent_id、traversal_ids等の階層管理 |
| 1-2 | namespace.rb | `app/models/namespace.rb` | 名前空間の基本構造 |

**読解のコツ**: GroupはNamespaceを継承しており、parent_idにより親子関係を管理。traversal_idsで効率的な階層クエリを実現している。

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

コントローラーの移行処理エントリーポイントを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | groups_controller.rb | `app/controllers/groups_controller.rb` | transferアクションの処理フロー |

**主要処理フロー**:
1. **235-246行目**: transferアクション、TransferServiceの呼び出し
2. **24行目**: authorize_change_group!による権限チェック

#### Step 3: 移行サービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | transfer_service.rb | `app/services/groups/transfer_service.rb` | 移行処理の全体ロジック |

**主要処理フロー**:
- **22-36行目**: executeメソッドのメインフロー
- **59-87行目**: proceed_to_transfer、実際の移行処理
- **107-117行目**: ensure_allowed_transfer、移行可否チェック
- **182-199行目**: update_group_attributes、グループ属性の更新
- **265-269行目**: refresh_project_authorizations、権限再計算

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

```
GroupsController#transfer
    │
    ├─ authorize_change_group! (権限チェック)
    │
    └─ Groups::TransferService#execute
           ├─ ensure_allowed_transfer (移行可否チェック)
           │      ├─ group_is_already_root?
           │      ├─ same_parent?
           │      ├─ valid_policies?
           │      ├─ namespace_with_same_path?
           │      ├─ group_projects_contain_registry_images?
           │      ├─ transfer_to_subgroup?
           │      └─ group_with_namespaced_npm_packages?
           │
           └─ proceed_to_transfer
                  ├─ Group.transaction
                  │      ├─ update_group_attributes
                  │      ├─ ensure_ownership
                  │      ├─ update_integrations
                  │      ├─ update_crm_objects
                  │      └─ remove_namespace_commit_emails
                  │
                  ├─ publish_event (GroupTransferedEvent)
                  ├─ transfer_labels
                  ├─ transfer_status_data
                  ├─ post_update_hooks
                  │      ├─ refresh_project_authorizations
                  │      └─ refresh_descendant_groups
                  ├─ propagate_integrations
                  └─ update_pending_builds
```

### データフロー図

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

グループID ───▶ GroupsController ───▶ リダイレクト
移行先ID              │
                      ▼
              TransferService
                      │
                      ▼
              ┌───────────────────┐
              │ トランザクション     │
              │                   │
              │ namespaces更新    │
              │ integrations更新  │
              │ CRM移行          │
              └───────────────────┘
                      │
                      ▼
              ラベル移行 ───▶ labels更新
                      │
                      ▼
              権限再計算 ───▶ project_authorizations更新
                      │
                      ▼
              GroupTransferedEvent ───▶ イベントストア
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| groups_controller.rb | `app/controllers/groups_controller.rb` | コントローラー | リクエスト処理のエントリーポイント |
| transfer_service.rb | `app/services/groups/transfer_service.rb` | サービス | グループ移行処理 |
| update_shared_runners_service.rb | `app/services/groups/update_shared_runners_service.rb` | サービス | 共有Runner設定更新 |
| group.rb | `app/models/group.rb` | モデル | グループエンティティ |
| namespace.rb | `app/models/namespace.rb` | モデル | 名前空間基底クラス |
| labels/transfer_service.rb | `app/services/labels/transfer_service.rb` | サービス | ラベル移行 |
| propagate_integration_worker.rb | `app/workers/propagate_integration_worker.rb` | ワーカー | インテグレーション伝播 |
