# 通知設計書 34-note_issue_email

## 概要

本ドキュメントは、GitLabにおけるIssueへのコメント通知（note_issue_email）の設計を記述したものである。

### 本通知の処理概要

**業務上の目的・背景**：Issueはタスク管理やバグ報告の中心的な機能であり、Issue上でのコメントによる議論はプロジェクト運営に不可欠である。Issueにコメントが追加された際に関係者に通知することで、タスクの進捗確認や問題解決のコミュニケーションを迅速化できる。

**通知の送信タイミング**：ユーザーがIssueにコメント（Note）を追加した時点で送信される。NotificationService#new_noteを経由し、noteable_typeに応じてnote_issue_emailが選択される。

**通知の受信者**：コメントが追加されたIssueに関連する以下のユーザーが受信者となる。Issue作成者、アサイン先、既存のディスカッション参加者、メンションされたユーザー、Issueにサブスクライブしているユーザー、プロジェクトのwatch設定を持つユーザー。受信者はNotificationRecipients::BuildService.build_new_note_recipientsによって決定される。

**通知内容の概要**：コメントが追加されたIssueの情報（タイトル、参照番号）、コメントの内容、コメント投稿者の情報が含まれる。コンフィデンシャルIssueの場合は、そのことを示すヘッダーが追加される。

**期待されるアクション**：受信者はコメント内容を確認し、必要に応じて返信を行う。質問への回答、追加情報の提供、タスクの状態更新などのアクションを実行する。

## 通知種別

メール

## 送信仕様

### 基本情報

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

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

NotificationRecipients::BuildService.build_new_note_recipientsを使用して受信者を決定する。具体的には以下の条件でフィルタリングされる：

1. Issue作成者
2. Issueのアサイン先
3. 既存のディスカッション参加者
4. コメント内でメンションされたユーザー
5. Issueをサブスクライブしているユーザー
6. プロジェクトのwatch設定を持つユーザー
7. 通知が無効化されていないユーザー

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | GitLab設定のデフォルト送信元 |
| 送信元名称 | {コメント投稿者名} (@{コメント投稿者ユーザー名}) |
| 件名 | Re: {プロジェクト名} \| {Issueタイトル} ({Issue参照番号}) |
| 形式 | HTML/テキスト（両形式） |

### 本文テンプレート

**HTML版：**
```
{コメント投稿者名} commented:（または started a new discussion / commented on a discussion）

{コメント本文（Markdown形式）}
```

**テキスト版：**
```
{コメント投稿者名} commented: {コメントURL}

{コメント本文}
```

### 添付ファイル

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

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @note | Noteオブジェクト | Note.find(note_id) | Yes |
| @issue | Issueオブジェクト | @note.noteable | Yes |
| @project | プロジェクト | @note.project | Yes |
| @target_url | コメントへのURL | Gitlab::UrlBuilder.build + anchor | Yes |
| @recipient | 受信者ユーザー | User.find(recipient_id) | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | Issue詳細画面でコメント投稿 | コメントが正常に保存された場合 | WebUI経由でのコメント |
| API | Notes API呼び出し（Issue向け） | コメントが正常に保存された場合 | API経由でのコメント |
| メール | メール返信によるコメント追加 | コメントが正常に保存された場合 | メール返信機能 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| システムノート | system_note_with_references?がtrueの場合 |
| noteable_typeが未設定 | コメント対象が不明な場合 |
| 受信者の通知設定がDisabled | ユーザーが通知を無効化している場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Issueにコメント追加] --> B[Note.create]
    B --> C[NotificationService#new_note]
    C --> D{noteable_type確認}
    D -->|Issue| E[note_issue_email選択]
    D -->|Other| F[他のメール種別]
    E --> G[send_new_note_notifications]
    G --> H[NotificationRecipients::BuildService.build_new_note_recipients]
    H --> I[受信者リスト生成]
    I --> J[各受信者にメール送信]
    J --> K[Notify.note_issue_email.deliver_later]
    K --> L[Sidekiqでメール送信実行]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| notes | コメント情報取得 | - |
| issues | Issue情報取得 | コンフィデンシャル判定含む |
| users | ユーザー情報取得 | - |
| projects | プロジェクト情報取得 | - |
| notification_settings | 通知設定取得 | - |

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

#### notes

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | Note識別 | note_id |
| note | コメント本文 | - |
| author_id | コメント投稿者 | - |
| noteable_type | 対象種別 | 'Issue' |
| noteable_id | 対象IssueID | - |

#### issues

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | Issue識別 | noteable_id |
| title | Issueタイトル | - |
| iid | Issue参照番号 | - |
| confidential | コンフィデンシャル判定 | - |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| sent_notifications | INSERT | 送信記録の保存 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | SMTPエラー | Sidekiqリトライ |
| テンプレートエラー | 変数が見つからない | 例外ログ記録 |
| 宛先不正 | メールアドレス形式エラー | スキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト |
| リトライ間隔 | 指数バックオフ |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | ApplicationRateLimiter設定に従う |
| 1日あたり上限 | 設定なし |

### 配信時間帯

制限なし

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

- コンフィデンシャルIssueの場合、X-GitLab-ConfidentialIssueヘッダーが設定される
- 受信者は通知設定とIssueへのアクセス権限に基づいてフィルタリングされる

## 備考

- コンフィデンシャルIssueの場合、confidentiality: @issue.confidential?がnote_thread_optionsに渡される

---

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | note.rb | `app/models/note.rb` | Noteモデル、noteable_type='Issue' |
| 1-2 | issue.rb | `app/models/issue.rb` | Issueモデル、confidential属性 |

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

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

**主要処理フロー**:
- **13-26行目**: `note_issue_email` - メール生成
- **16行目**: `@issue = @note.noteable` - Issue取得
- **17行目**: `Gitlab::UrlBuilder.build` - URL生成
- **18-25行目**: `mail_answer_note_thread` - コンフィデンシャル考慮

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

```
Note.create (Issueへのコメント)
    │
    └─ NotificationService#new_note
           │
           └─ send_new_note_notifications
                  │
                  ├─ notify_method = :note_issue_email
                  │
                  └─ Notify.note_issue_email
                         │
                         ├─ setup_note_mail
                         │
                         └─ mail_answer_note_thread
                                └─ confidentiality: @issue.confidential?
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notes.rb | `app/mailers/emails/notes.rb` | ソース | Note関連メーラー |
| note_issue_email.html.haml | `app/views/notify/note_issue_email.html.haml` | テンプレート | HTML版 |
| note_issue_email.text.erb | `app/views/notify/note_issue_email.text.erb` | テンプレート | テキスト版 |
| _note_email.html.haml | `app/views/notify/_note_email.html.haml` | テンプレート | 共通パーシャル |
