# 通知設計書 116-import_source_user_rejected

## 概要

本ドキュメントは、GitLabにおけるインポートソースユーザー再割り当て拒否通知（import_source_user_rejected）の設計仕様を記述する。

### 本通知の処理概要

インポートソースユーザー再割り当て拒否通知は、再割り当て対象ユーザーが自分への貢献の再割り当てを拒否した際に、再割り当てをリクエストしたグループオーナーに対してその結果を通知するメールを送信する。

**業務上の目的・背景**：ユーザーマッピングの再割り当てにはターゲットユーザーの承認が必要であり、拒否される可能性がある。拒否された場合、再割り当てをリクエストしたグループオーナーは別のユーザーへの再割り当てを検討するか、プレースホルダーのままにするかの判断が必要である。本通知により、拒否の事実をグループオーナーに即座に報告し、次のアクションを促す。

**通知の送信タイミング**：対象ユーザーが再割り当てを拒否し、RejectReassignmentServiceが正常に完了した時点で、同期的に送信される（deliver_now）。

**通知の受信者**：再割り当てをリクエストしたユーザー（reassigned_by_user）に送信される。

**通知内容の概要**：拒否したユーザーの情報、元のプレースホルダー情報、およびグループメンバーページへのリンクを含む。

**期待されるアクション**：受信者はグループのメンバーページにアクセスし、別のユーザーへの再割り当てを検討するか、プレースホルダーを維持するかを決定する。

## 通知種別

メール

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（deliver_now） |
| 優先度 | 高（即時送信） |
| リトライ | なし（同期送信のため） |

### 送信先決定ロジック

ImportSourceUserレコードのreassigned_by_userのnotification_email_or_defaultに送信される。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | GitLabインスタンス設定に従う |
| 送信元名称 | GitLab |
| 件名 | `Reassignments in {group_full_path} rejected` |
| 形式 | テキスト |

### 本文テンプレート

```
Reassignment rejected

{reassign_to_name} ({reassign_to_username}) has declined your request to reassign contributions from {source_name} ({source_username}) on {source_hostname} to them in {destination_group}. To reassign these contributions to another user, go to the "Members" page of {destination_group}.
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 添付ファイルはない |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @source_user | インポートソースユーザー | Import::SourceUser.find(source_user_id) | Yes |
| source_hostname | インポート元ホスト名 | @source_user.source_hostname | Yes |
| source_name | 元ユーザー名 | @source_user.source_name | Yes |
| source_username | 元ユーザー名（@付き） | @source_user.source_username | Yes |
| reassign_to_user | 拒否したユーザー | @source_user.reassign_to_user | Yes |
| destination_group | インポート先グループ | @source_user.namespace | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サービス呼び出し | send_user_reassign_rejected_email | reject状態への遷移成功時 | 拒否処理成功時に即座に送信 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 状態遷移失敗 | rejected状態への遷移に失敗した場合 |
| 権限エラー | 拒否操作の権限がない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ターゲットユーザーが再割り当て拒否] --> B[RejectReassignmentService実行]
    B --> C{状態遷移成功?}
    C -->|Yes| D[send_user_reassign_rejected_email]
    C -->|No| E[エラー応答]
    D --> F[Notify.import_source_user_rejected.deliver_now]
    F --> G[ServiceResponse.success]
    G --> H[終了]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| import_source_users | ソースユーザー情報取得 | |
| users | 再割り当て先ユーザー、実行者情報取得 | |
| namespaces | 名前空間情報取得 | |

### テーブル別参照項目詳細

#### import_source_users

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | ソースユーザー特定 | Import::SourceUser.find(source_user_id) |
| source_hostname | インポート元ホスト名 | |
| source_name | 元ユーザー名 | |
| source_username | 元ユーザー名 | |
| reassign_to_user_id | 拒否したユーザーID | |
| reassigned_by_user_id | 再割り当てリクエスト者ID（送信先） | |
| namespace_id | 名前空間ID | |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| import_source_users | UPDATE | statusをrejectedに更新（サービス側で実施） |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| SourceUser未存在 | Import::SourceUser.findで見つからない | ActiveRecord::RecordNotFoundが発生 |
| 権限エラー | current_userがreassign_to_userと一致しない | error_invalid_permissionsを返す |
| 状態エラー | awaiting_approval以外からの遷移 | error_invalid_statusを返す |
| メール送信失敗 | SMTPエラー等 | 例外が発生（同期送信のため） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（同期送信） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | システム設定に従う |
| 1日あたり上限 | システム設定に従う |

### 配信時間帯

制限なし（24時間送信可能）

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

- 拒否操作はreassign_to_userかつ有効なreassignment_tokenを持つユーザーのみ実行可能
- トークン検証により不正な拒否を防止

## 備考

- deliver_nowを使用する理由：拒否操作は即座にフィードバックを必要とするアクションであるため
- 拒否後、ImportSourceUserはrejected状態になり、再度の再割り当てリクエストが可能

---

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

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

### 推奨読解順序

#### Step 1: 拒否サービスを理解する

拒否処理の全体フローを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | reject_reassignment_service.rb | `app/services/import/source_users/reject_reassignment_service.rb` | execute、send_user_reassign_rejected_email |

**読解のコツ**: with_lockでの排他制御、状態遷移の条件チェックに注目

**主要処理フロー**:
1. **12-33行目**: executeメソッドで状態チェック、ロック取得、拒否処理
2. **36-38行目**: send_user_reassign_rejected_emailでdeliver_now呼び出し

#### Step 2: メーラー実装を理解する

メール本文生成ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | imports.rb | `app/mailers/emails/imports.rb` | import_source_user_rejectedメソッド（112-123行目） |

**主要処理フロー**:
- **112-123行目**: SourceUserの取得、reassigned_by_userへの送信

#### Step 3: テンプレートを理解する

メール本文テンプレートを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | import_source_user_rejected.text.erb | `app/views/notify/import_source_user_rejected.text.erb` | 変数展開、グループメンバーリンク |

**主要処理フロー**:
- **1-7行目**: テンプレート変数の準備
- **9-17行目**: 拒否メッセージとグループメンバーページへの案内

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

```
RejectReassignmentService#execute
    │
    ├─ import_source_user.awaiting_approval?
    │
    ├─ import_source_user.with_lock
    │      │
    │      ├─ current_user_matches_reassign_to_user?
    │      ├─ reassignment_token_is_valid?
    │      │
    │      └─ import_source_user.reject!
    │
    ├─ send_user_reassign_rejected_email
    │      │
    │      └─ Notify.import_source_user_rejected.deliver_now
    │             │
    │             ├─ Import::SourceUser.find
    │             │      ├─ reassign_to_user
    │             │      ├─ reassigned_by_user（送信先）
    │             │      └─ namespace
    │             │
    │             └─ email_with_layout
    │
    └─ track_reassignment_event
```

### データフロー図

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

ターゲットユーザー            RejectReassignmentService
の拒否アクション ──────────▶
                                      │
source_user_id ───────────────────────┤
reassignment_token ───────────────────┤
                                      │
                                      ▼
                               状態遷移（rejected）
                                      │
                                      ▼
ImportSourceUser ─────────────▶ Notify.import_source_user_rejected ───▶ メール送信
    │                                                                    │
    ├─ source_hostname ────────────────────────────────────────────────┤
    ├─ source_name ────────────────────────────────────────────────────┤
    ├─ source_username ────────────────────────────────────────────────┤
    ├─ reassign_to_user（拒否者） ─────────────────────────────────────┤
    ├─ reassigned_by_user（送信先） ───────────────────────────────────┤
    └─ namespace ──────────────────────────────────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| reject_reassignment_service.rb | `app/services/import/source_users/reject_reassignment_service.rb` | ソース | 拒否処理サービス |
| import_source_user.rb | `app/models/import/source_user.rb` | ソース | ImportSourceUserモデル |
| imports.rb | `app/mailers/emails/imports.rb` | ソース | メーラーメソッド定義 |
| import_source_user_rejected.text.erb | `app/views/notify/import_source_user_rejected.text.erb` | テンプレート | メール本文テンプレート |
| imports_spec.rb | `spec/mailers/emails/imports_spec.rb` | テスト | メーラーのテスト仕様 |
