# 通知設計書 101-service_desk_new_participant_email

## 概要

本ドキュメントは、GitLabのService Desk機能において、新しい参加者がチケットに追加された際に送信される通知メールの設計を記載するものです。

### 本通知の処理概要

本通知は、Service Deskのチケット（Issue）に新しいメール参加者が追加されたことを、その参加者に知らせるための通知です。

**業務上の目的・背景**：Service Deskは外部ユーザーからのサポートリクエストをメール経由で受け付ける機能です。チケットに関連する議論に新しい参加者が追加された場合、その参加者が今後のやり取りに参加できるよう、追加された旨を通知する必要があります。これにより、参加者は自分がチケットに関与していることを認識し、メール返信による議論への参加が可能になります。

**通知の送信タイミング**：IssueEmailParticipants::CreateServiceにて、新しいメール参加者がIssueに追加され、データベースへの永続化が成功した直後に非同期で送信されます（49行目: `Notify.service_desk_new_participant_email(target.id, new_participant).deliver_later`）。

**通知の受信者**：新しく追加されたメール参加者本人です。`IssueEmailParticipant`モデルのemailフィールドに設定されたメールアドレスに送信されます。

**通知内容の概要**：チケット番号、返信方法の説明、購読解除の案内が含まれます。参加者はメール返信でディスカッションに参加でき、他の参加者にも通知が届くことが説明されます。

**期待されるアクション**：受信者は通知を確認し、必要に応じてメール返信でチケットの議論に参加することが期待されます。また、今後の通知を受け取りたくない場合は購読解除を行うことができます。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（deliver_later） |
| 優先度 | 中 |
| リトライ | Sidekiqのデフォルト設定に準拠 |

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

新しく追加されたIssueEmailParticipantレコードのemailフィールドに設定されたメールアドレスに送信されます。`IssueEmailParticipants::CreateService`内で、参加者がデータベースに正常に永続化された場合にのみ通知が送信されます。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | Service Deskのカスタムメール設定またはデフォルトのSupport Botメール |
| 送信元名称 | `service_desk_setting.outgoing_name`またはデフォルト |
| 件名 | `Re: {Issueタイトル} (#{Issue番号})` |
| 形式 | HTML/テキスト（マルチパート） |

### 本文テンプレート

#### HTML版（service_desk_new_participant_email.html.haml）
```
You have been added to ticket #{Issue番号}.

Reply to this email to participate in the discussion. All other participants of the ticket will receive notifications of your reply.

You can unsubscribe from further updates to this ticket. To receive updates in the future, you will have to be added again as a participant.
```

#### テキスト版（service_desk_new_participant_email.text.erb）
```
You have been added to ticket #{Issue番号}.

Reply to this email to participate in the discussion. All other participants of the ticket will receive notifications of your reply.

You can unsubscribe from further updates to this ticket. To receive updates in the future, you will have to be added again as a participant.
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知には添付ファイルはありません |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @work_item | 対象のWorkItem（Issue） | WorkItem.find(work_item_id) | Yes |
| @work_item.iid | Issueの内部ID | work_items.iid | Yes |
| @project | 対象プロジェクト | @work_item.project | Yes |
| @support_bot | Support Botユーザー | Users::Internal.support_bot | Yes |
| @service_desk_setting | Service Desk設定 | project.service_desk_setting | No |
| @sent_notification | 送信通知レコード | SentNotification.record(...) | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API/画面操作 | IssueEmailParticipants::CreateService.execute | 参加者がDBに正常に保存された | 新しいメール参加者の追加時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Feature.disabled?(:issue_email_participants) | 機能フラグが無効の場合は処理自体が中止される |
| 参加者の永続化失敗 | new_participant.persisted?がfalseの場合は送信されない |
| 重複メールアドレス | 既に参加者として登録済みのメールアドレスは追加されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[IssueEmailParticipants::CreateService.execute] --> B{Feature Flag有効?}
    B -->|No| C[エラー返却]
    B -->|Yes| D{権限チェック}
    D -->|No| E[エラー返却]
    D -->|Yes| F{メールアドレス存在?}
    F -->|No| G[エラー返却]
    F -->|Yes| H[重複除去・制限数適用]
    H --> I[参加者レコード作成]
    I --> J{永続化成功?}
    J -->|No| K[次のメールアドレスへ]
    J -->|Yes| L[Notify.service_desk_new_participant_email]
    L --> M[deliver_later]
    M --> N[メトリクス記録]
    N --> K
    K --> O{全メール処理完了?}
    O -->|No| I
    O -->|Yes| P[システムノート追加]
    P --> Q[終了]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| work_items | 対象Issue/WorkItemの取得 | |
| projects | プロジェクト情報の取得 | |
| service_desk_settings | Service Desk設定の取得 | カスタムメール設定等 |
| users | Support Botユーザーの取得 | Users::Internal.support_bot |
| issue_email_participants | 既存参加者の確認・新規参加者の登録 | |

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

#### work_items

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | WorkItemの一意識別子 | WHERE id = work_item_id |
| iid | Issue番号（件名に使用） | |
| title | Issueタイトル（件名に使用） | |
| project_id | プロジェクトの特定 | |
| external_author | 外部作成者のメールアドレス | |

#### issue_email_participants

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| email | 参加者メールアドレス | WHERE issue_id = ? |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| issue_email_participants | INSERT | 新しい参加者レコードの作成 |
| sent_notifications | INSERT | 送信通知レコードの作成 |

#### issue_email_participants

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | issue_id | @work_item.id | WorkItem ID |
| INSERT | email | 追加されるメールアドレス | |
| INSERT | created_at | 現在時刻 | |
| INSERT | updated_at | 現在時刻 | |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 機能フラグ無効 | Feature.disabled?(:issue_email_participants) | ServiceResponse.errorを返却 |
| 権限不足 | user_privileged?がfalse | ServiceResponse.errorを返却 |
| メールアドレス未指定 | emails.blank? | ServiceResponse.errorを返却 |
| 参加者数上限超過 | 既存参加者数 >= MAX_NUMBER_OF_RECORDS(10) | ログ出力後、追加をスキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト（25回） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | 一時的なネットワークエラー、SMTP接続エラー等 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | GitLab全体のメール送信レート制限に準拠 |
| 1日あたり上限 | GitLab全体のメール送信レート制限に準拠 |

### 配信時間帯

特に制限なし（イベント発生時に即時送信）

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

- メールアドレスは小文字に正規化して重複チェックが行われる
- 1つのIssueに追加できる参加者数は最大10名に制限されている（MAX_NUMBER_OF_RECORDS）
- 1回のリクエストで追加できるメールアドレス数も制限されている（MAX_NUMBER_OF_EMAILS）
- Service Deskのカスタムメール機能が有効な場合、SMTP認証情報を使用して送信される

## 備考

- Service Deskレイアウト（`service_desk`）が適用される
- カスタムメールテンプレート（`new_participant`）が設定されている場合はそちらが優先される
- SentNotificationレコードが作成され、メール返信時の追跡に使用される

---

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

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

### 推奨読解順序

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

まず、通知に関連するデータモデルと設定を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | issue_email_participant.rb | `app/models/issue_email_participant.rb` | メール参加者モデルの構造、バリデーション |
| 1-2 | service_desk_setting.rb | `app/models/service_desk_setting.rb` | Service Desk設定の構造 |

**読解のコツ**: Railsモデルのバリデーションとアソシエーションを確認し、データの制約を理解する。

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

処理の起点となるサービスクラスを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | create_service.rb | `app/services/issue_email_participants/create_service.rb` | 参加者追加の主要ロジック |

**主要処理フロー**:
1. **9-21行目**: executeメソッド - Feature Flag、権限、メールアドレスのチェック
2. **26-29行目**: deduplicate_and_limit_emails - 重複除去と制限適用
3. **32-54行目**: add_participants - 参加者追加とメール送信
4. **49行目**: Notify.service_desk_new_participant_email - 通知送信の実行

#### Step 3: メーラーを理解する

通知メールの生成処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | service_desk.rb | `app/mailers/emails/service_desk.rb` | service_desk_new_participant_emailメソッドの実装 |

**主要処理フロー**:
- **67-86行目**: service_desk_new_participant_email - メール生成処理
- **70-75行目**: sender設定 - 送信元の決定
- **77-82行目**: options設定 - 件名、宛先、本文の設定
- **84行目**: mail_new_thread - メール送信

#### Step 4: ビューテンプレートを理解する

メール本文のテンプレートを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | service_desk_new_participant_email.html.haml | `app/views/notify/service_desk_new_participant_email.html.haml` | HTML形式のメール本文 |
| 4-2 | service_desk_new_participant_email.text.erb | `app/views/notify/service_desk_new_participant_email.text.erb` | テキスト形式のメール本文 |

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

```
IssueEmailParticipants::CreateService#execute
    │
    ├─ Feature.enabled?(:issue_email_participants)
    │
    ├─ user_privileged?
    │
    ├─ deduplicate_and_limit_emails
    │      └─ existing_emails（重複チェック）
    │
    └─ add_participants
           │
           ├─ IssueEmailParticipant.create
           │
           ├─ Notify.service_desk_new_participant_email
           │      │
           │      ├─ setup_service_desk_mail
           │      │      ├─ WorkItem.find
           │      │      ├─ Users::Internal.support_bot
           │      │      └─ SentNotification.record
           │      │
           │      ├─ sender（送信元設定）
           │      │
           │      ├─ service_desk_template_content_options
           │      │
           │      └─ mail_new_thread
           │             └─ inject_service_desk_custom_email
           │
           └─ Gitlab::Metrics::BackgroundTransaction.add_event
```

### データフロー図

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

emails（追加する           IssueEmailParticipants          issue_email_participants
メールアドレス）    ───▶   ::CreateService#execute    ───▶  テーブルへのINSERT
                                   │
                                   │
target（Issue）            ───▶    │                    ───▶  通知メール送信
                                   │                          （deliver_later経由）
                                   │
current_user               ───▶    │                    ───▶  システムノート追加
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| create_service.rb | `app/services/issue_email_participants/create_service.rb` | ソース | 参加者追加サービス |
| service_desk.rb | `app/mailers/emails/service_desk.rb` | ソース | Service Deskメーラー |
| service_desk_new_participant_email.html.haml | `app/views/notify/service_desk_new_participant_email.html.haml` | テンプレート | HTML本文テンプレート |
| service_desk_new_participant_email.text.erb | `app/views/notify/service_desk_new_participant_email.text.erb` | テンプレート | テキスト本文テンプレート |
| issue_email_participant.rb | `app/models/issue_email_participant.rb` | ソース | 参加者モデル |
| service_desk_setting.rb | `app/models/service_desk_setting.rb` | ソース | Service Desk設定モデル |
| notify.rb | `app/mailers/notify.rb` | ソース | メーラー基底クラス |
