# 機能設計書 110-バルクインポート

## 概要

本ドキュメントは、GitLabのバルクインポート（Direct Transfer）機能についての機能設計書である。

### 本機能の処理概要

バルクインポートは、別のGitLabインスタンスからグループやプロジェクトを直接転送（Direct Transfer）する機能である。従来のファイルベースのインポート/エクスポートとは異なり、GraphQL APIとREST APIを使用してソースインスタンスからデータを直接取得し、ターゲットインスタンスにインポートする。サブグループとプロジェクトも含めて階層的にインポートできる。

**業務上の目的・背景**：GitLabインスタンス間でのグループ・プロジェクト移行を効率化する。ファイルのアップロード/ダウンロードが不要で、大規模な移行に適している。

**機能の利用シーン**：
- 別のGitLabインスタンスへの組織移行
- セルフマネージドからGitLab SaaSへの移行
- 複数グループの一括移行
- プロジェクトを含むグループ構造の移行

**主要な処理内容**：
1. ソースGitLabインスタンスへの接続設定
2. インポート可能なグループ一覧の取得
3. インポート対象の選択とパラメータ設定
4. BulkImportレコードとEntityの作成
5. 非同期でのデータインポート（バックグラウンドワーカー）
6. サブグループ・プロジェクトの再帰的インポート

**関連システム・外部連携**：
- ソースGitLab インスタンス（GraphQL/REST API）
- Sidekiq（BulkImportWorker、SourceUsersAttributesWorker）
- Gitaly（リポジトリ操作）

**権限による制御**：
- ターゲット名前空間への`create_subgroup`または`import_projects`権限が必要
- ソースインスタンスでOwner以上のアクセス権限が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 302 | グループ移行 | 主画面 | バルクインポート設定・実行 |
| 303 | グループ移行状態 | 主画面 | バルクインポート進捗表示 |

## 機能種別

データインポート / 外部API連携 / バックグラウンド処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| bulk_import_gitlab_url | String | Yes | ソースGitLab URL | ブロックされたURLでないこと |
| bulk_import_gitlab_access_token | String | Yes | アクセストークン | 有効なトークン |
| source_type | String | Yes | ソースタイプ | 'group_entity'のみ許可 |
| source_full_path | String | Yes | ソースグループパス | 存在するグループ |
| destination_slug | String | Yes | 宛先スラッグ | 英数字、ハイフン、アンダースコア |
| destination_namespace | String | No | 宛先名前空間パス | 有効な名前空間 |
| migrate_projects | Boolean | No | プロジェクト移行有無 | デフォルトtrue |
| migrate_memberships | Boolean | No | メンバーシップ移行有無 | デフォルトtrue |
| filter | String | No | グループフィルタ | 検索キーワード |

### 入力データソース

- バルクインポート設定画面
- API呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| bulk_import | Object | BulkImportレコード |
| entities | Array | インポートされたエンティティ一覧 |
| status_name | String | インポート状態 |
| has_failures | Boolean | 失敗有無 |

### 出力先

- グループ（GitLab内）
- プロジェクト（GitLab内）
- インポート状態画面

## 処理フロー

### 処理シーケンス

```
1. 接続設定
   └─ URL・アクセストークンをセッションに保存
2. 接続検証
   └─ URLブロック確認、バージョン・スコープ検証
3. グループ一覧取得
   └─ GraphQL APIでインポート可能グループを取得
4. インポート設定
   └─ ソースグループ・宛先の指定
5. バリデーション
   └─ パス検証、権限確認、重複確認
6. BulkImportレコード作成
   └─ BulkImport + Entityをトランザクションで作成
7. 非同期インポート開始
   └─ BulkImportWorker.perform_async
8. 進捗追跡
   └─ リアルタイム更新（ポーリング）
```

### フローチャート

```mermaid
flowchart TD
    A[バルクインポート開始] --> B[接続設定入力]
    B --> C{URLブロック確認}
    C -->|ブロック| D[エラー表示]
    C -->|OK| E[バージョン・スコープ検証]
    E -->|失敗| D
    E -->|成功| F[グループ一覧取得]
    F --> G[インポート対象選択]
    G --> H{レート制限?}
    H -->|Yes| I[429エラー]
    H -->|No| J{バリデーション}
    J -->|失敗| K[422エラー]
    J -->|成功| L[BulkImport作成]
    L --> M[Entity作成]
    M --> N[BulkImportWorker起動]
    N --> O[非同期インポート実行]
    O --> P{サブグループ?}
    P -->|Yes| Q[サブグループEntity作成]
    Q --> O
    P -->|No| R[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-110-01 | ソースタイプ制限 | group_entityのみ許可（UIから） | create時 |
| BR-110-02 | 最小アクセスレベル | ソースでOwner権限必要 | グループ一覧取得 |
| BR-110-03 | トップレベルのみ取得 | 一覧ではトップレベルグループのみ表示 | グループ一覧 |
| BR-110-04 | 宛先重複禁止 | 既存グループ/プロジェクトと重複不可 | Entity作成時 |
| BR-110-05 | レート制限 | bulk_importスコープでレート制限適用 | create時 |
| BR-110-06 | 機能有効化必須 | bulk_import_enabled設定またはフィーチャーフラグ | 全操作 |

### 計算ロジック

クエリパラメータ:
- top_level_only: true（トップレベルのみ）
- min_access_level: Owner (50)
- search: フィルタキーワード（オプション）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| BulkImport作成 | bulk_imports | INSERT | インポートジョブレコード |
| Entity作成 | bulk_import_entities | INSERT | インポートエンティティ |
| Configuration作成 | bulk_import_configurations | INSERT | 接続設定保存 |
| グループ作成 | groups | INSERT | インポートされたグループ |
| プロジェクト作成 | projects | INSERT | インポートされたプロジェクト |

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

#### bulk_imports テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | - |
| INSERT | source_type | 'gitlab' | 固定値 |
| INSERT | source_version | client.instance_version | - |
| INSERT | source_enterprise | client.instance_enterprise | - |

#### bulk_import_entities テーブル

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | source_type | 'group_entity' / 'project_entity' | - |
| INSERT | source_full_path | ソースパス | - |
| INSERT | destination_slug | 宛先スラッグ | - |
| INSERT | destination_namespace | 宛先名前空間 | - |
| INSERT | migrate_projects | true/false | - |
| INSERT | migrate_memberships | true/false | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | NotFound | 機能無効 | 管理者設定を確認 |
| 422 | UnprocessableEntity | バリデーションエラー | 入力値を確認 |
| 429 | TooManyRequests | レート制限 | 時間をおいて再試行 |
| - | BlockedUrlError | ブロックされたURL | 許可されたURLを使用 |
| - | BulkImports::Error | 接続エラー等 | エラー内容を確認 |

### リトライ仕様

- BulkImportWorkerでの自動リトライ
- 接続エラー時はセッションクリアして再設定

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

- BulkImport + Entity作成はトランザクション内で実行
- 失敗時は全体ロールバック

## パフォーマンス要件

- ポーリング間隔: 3秒（POLLING_INTERVAL = 3000ms）
- グループ一覧: ページネーション対応（x-next-page等）
- 大規模インポートは複数ワーカーで並列処理

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

- URLブロック機能で不正なURLを拒否
- アクセストークンはセッションに保存
- 接続先検証（バージョン、スコープ）
- ローカルリクエスト制御（allow_local_requests_from_web_hooks_and_services）
- レート制限による保護

## 備考

- Direct Transferとも呼ばれる機能
- ユーザーマッピング機能でソースユーザーを対応付け
- SourceUsersAttributesWorkerでユーザー属性を事前取得
- サブグループ・プロジェクトは再帰的にインポート

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | bulk_imports_controller.rb | `app/controllers/import/bulk_imports_controller.rb` | コントローラの全体構造 |
| 1-2 | create_service.rb | `app/services/bulk_imports/create_service.rb` | サービスの処理フロー |

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

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

**主要処理フロー**:
1. **18-26行目**: `configure`アクションで接続設定保存
2. **28-52行目**: `status`アクションでグループ一覧表示
3. **60-78行目**: `create`アクションでインポート開始
4. **80-84行目**: `realtime_changes`でポーリング応答
5. **160-163行目**: `ensure_bulk_import_enabled`で機能有効確認
6. **173-187行目**: `verify_blocked_uri`でURLブロック確認

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create_service.rb | `app/services/bulk_imports/create_service.rb` | インポート作成処理 |

**主要処理フロー**:
1. **45-69行目**: `execute`メソッドでインポート実行
2. **75-80行目**: `validate!`でバリデーション
3. **82-114行目**: `create_bulk_import`でレコード作成（トランザクション）
4. **116-127行目**: `validate_source_full_path!`でソースパス検証
5. **162-174行目**: `validate_destination_namespace`で権限検証
6. **182-198行目**: `validate_destination_full_path`で重複チェック

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

```
Import::BulkImportsController
    │
    ├─ #configure
    │      ├─ verify_blocked_uri
    │      ├─ validate_configure_params!
    │      │      └─ BulkImports::Clients::HTTP
    │      │             ├─ validate_instance_version!
    │      │             └─ validate_import_scopes!
    │      └─ redirect_to status
    │
    ├─ #status
    │      └─ BulkImports::GetImportableDataService
    │             └─ GraphQL API
    │
    └─ #create
           │
           ├─ throttled_request?
           │      └─ Gitlab::ApplicationRateLimiter
           │
           └─ BulkImports::CreateService
                  │
                  ├─ validate!
                  │      ├─ validate_instance_version!
                  │      ├─ validate_import_scopes!
                  │      └─ validate_source_full_path!
                  │
                  ├─ create_bulk_import
                  │      ├─ BulkImport.create!
                  │      ├─ create_configuration!
                  │      └─ BulkImports::Entity.create!
                  │
                  └─ BulkImportWorker.perform_async
                         │
                         ├─ グループデータインポート
                         ├─ サブグループEntity作成
                         ├─ プロジェクトEntity作成
                         └─ 再帰的に処理
```

### データフロー図

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

ソースGitLab URL ───────▶ BulkImportsController ─────────▶ グループ
アクセストークン                │                              プロジェクト
インポート対象                  ├─ HTTP Client
                               │      │
                               │      └─ ソースGitLab
                               │            ├─ GraphQL API
                               │            └─ REST API
                               │
                               ├─ CreateService
                               │      │
                               │      ├─ BulkImport
                               │      └─ Entity
                               │
                               └─ BulkImportWorker
                                      │
                                      ├─ グループImporter
                                      └─ プロジェクトImporter
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| bulk_imports_controller.rb | `app/controllers/import/bulk_imports_controller.rb` | ソース | HTTPエンドポイント |
| create_service.rb | `app/services/bulk_imports/create_service.rb` | ソース | インポート作成サービス |
| http.rb | `lib/bulk_imports/clients/http.rb` | ソース | HTTPクライアント |
| graphql.rb | `lib/bulk_imports/clients/graphql.rb` | ソース | GraphQLクライアント |
| bulk_import_worker.rb | `app/workers/bulk_import_worker.rb` | ソース | バックグラウンドワーカー |
| bulk_import.rb | `app/models/bulk_import.rb` | ソース | BulkImportモデル |
| entity.rb | `app/models/bulk_imports/entity.rb` | ソース | Entityモデル |
