# 通知設計書 100-service_desk_new_note_email

## 概要

本ドキュメントは、GitLabにおけるService Desk新規コメント通知（service_desk_new_note_email）の設計仕様を記述する。Service Deskのチケットに新しいコメントが追加された際に、チケットの外部参加者に対して送信されるメール通知の仕組みを定義する。

### 本通知の処理概要

**業務上の目的・背景**：Service Deskでは外部ユーザーとプロジェクトメンバー間でメールベースのコミュニケーションが行われる。チケットに新しいコメントが追加された際に外部参加者に通知することで、双方向のコミュニケーションを実現し、問題解決までの応答性を向上させる。

**通知の送信タイミング**：Service Deskチケットに新しいノート（コメント）が追加され、NotificationServiceのsend_service_desk_notificationメソッドが呼び出されたタイミングで通知がトリガーされる。コメント作成者自身への通知は除外される。

**通知の受信者**：チケットに関連付けられた外部参加者（IssueEmailParticipant）が受信対象となる。ただし、ノートの作成者が外部参加者自身の場合は通知から除外される。

**通知内容の概要**：コメント投稿者の名前、コメント本文、添付ファイル（存在する場合）が含まれる。カスタムテンプレートが設定されている場合は、プロジェクト固有のフォーマットでコメントを通知できる。

**期待されるアクション**：外部参加者は通知を受け取り、コメント内容を確認し、必要に応じてメールで返信することでコミュニケーションを継続することが期待される。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（deliver_later）、スレッド返信（mail_answer_thread） |
| 優先度 | 高 |
| リトライ | Sidekiqのデフォルト設定に従う |

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

1. チケットに関連付けられたIssueEmailParticipantを取得
2. ノート作成者が外部参加者の場合は除外
3. 各参加者のメールアドレスに送信

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | カスタムメール有効時はカスタムアドレス、無効時はデフォルト |
| 送信元名称 | コメント投稿者の名前 |
| 件名 | `{work_item_title} (#{work_item_iid})` |
| 形式 | HTML/テキスト |

### 本文テンプレート

```
[投稿者名] wrote: (設定により表示)

[コメント本文（Markdown処理済み）]
```

テキスト形式：
```
New response for issue #{issue_iid}:

Author: {author_name}

{note_text}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| ノート内の添付ファイル | 各種 | 合計10MB以下 | ノートに含まれるアップロードファイル |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @note | ノートオブジェクト | Note.find(note_id) | Yes |
| @work_item | Work Itemオブジェクト | WorkItem.find(work_item_id) | Yes |
| @project | プロジェクト | @work_item.project | Yes |
| @support_bot | サポートBot | Users::Internal.support_bot | Yes |
| @service_desk_setting | Service Desk設定 | @project.service_desk_setting | No |
| @uploads_to_attach | 添付ファイルリスト | 抽出されたアップロード | No |
| @uploads_as_attachments | 添付ファイルパス | 置換用パスリスト | No |
| %{NOTE_TEXT} | ノート本文 | @note.note | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サービス呼び出し | ノート作成 | Service Deskチケット、外部参加者存在 | NotificationService#send_service_desk_notification |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 外部参加者がノート作成者 | 自分のコメントへの通知は除外 |
| 外部参加者なし | 通知対象者がいない場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ノート作成] --> B[NotificationService#send_service_desk_notification]
    B --> C[外部参加者リスト取得]
    C --> D{参加者存在?}
    D -->|No| E[終了]
    D -->|Yes| F[recipients.each]
    F --> G{ノート作成者と同一?}
    G -->|Yes| H[スキップ]
    G -->|No| I[service_desk_new_note_email生成]
    I --> J[setup_service_desk_mail]
    J --> K[setup_service_desk_attachments]
    K --> L[mail_answer_thread]
    L --> M[add_uploads_as_attachments]
    M --> N[inject_service_desk_custom_email]
    N --> O[deliver_later]
    O --> P[メトリクス記録]
    H --> F
    P --> F
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| notes | ノート情報 | note, author_id |
| work_items | チケット情報 | iid, title |
| projects | プロジェクト情報 | プロジェクト名 |
| service_desk_settings | Service Desk設定 | custom_email |
| issue_email_participants | 外部参加者情報 | email |
| users | ユーザー情報 | ノート作成者 |
| uploads | アップロード情報 | 添付ファイル |
| note_metadata | ノートメタデータ | email_participant |

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

#### notes

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| note | コメント本文 | 直接参照 |
| author_id | 投稿者ID | 直接参照 |

#### issue_email_participants

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

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| sent_notifications | INSERT | 送信通知記録 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | SMTPエラー | Sidekiqによるリトライ |
| Note不存在 | Note.find失敗 | 例外発生 |
| 添付ファイル取得失敗 | アップロード読み取りエラー | エラートラッキング、処理継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト（25回） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | SMTP接続エラー、タイムアウト |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし（個別通知） |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（リアルタイム送信）

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

- 外部ユーザーへの送信のため、内部参照等を除去（issuable_reference_expansion_enabled）
- 添付ファイルサイズ制限（10MB）
- ノート作成者が外部参加者の場合、自身への通知を除外してループ防止
- カスタムメール機能では認証情報を暗号化して保存

## 備考

- service_deskレイアウトを使用（override_layout_lookup_table）
- 添付ファイルはノート内のMarkdownパターンから抽出
- Gitlab::Metrics::BackgroundTransactionでメトリクス記録（service_desk_new_note_email）
- カスタムテンプレート（new_note）でプロジェクト固有のフォーマットを設定可能
- uploads_as_attachmentsでMarkdown内のパスを実際の添付に置換

---

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

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

### 推奨読解順序

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

Service Desk関連のモデルと外部参加者の仕組みを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | issue_email_participant.rb | `app/models/issue_email_participant.rb` | 外部参加者モデル |

**読解のコツ**: IssueEmailParticipantはWork Itemに関連付けられ、外部ユーザーのメールアドレスを保持する。

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

NotificationServiceでの呼び出し処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | notification_service.rb | `app/services/notification_service.rb` | send_service_desk_notificationメソッド（900-914行目） |

**主要処理フロー**:
1. **904行目**: note_external_author取得（外部参加者からのノート判定）
2. **906行目**: recipients.each
3. **909行目**: 外部参加者自身への通知除外チェック
4. **911行目**: service_desk_new_note_email送信
5. **912行目**: メトリクス記録

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

通知メールの構築ロジックを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | service_desk.rb | `app/mailers/emails/service_desk.rb` | service_desk_new_note_emailメソッド（40-65行目） |

**主要処理フロー**:
- **41行目**: Note.find(note_id)
- **43行目**: setup_service_desk_mail
- **45行目**: setup_service_desk_attachments（添付ファイル準備）
- **47-51行目**: sender設定
- **53-58行目**: オプション設定
- **60行目**: mail_answer_thread
- **62-63行目**: 添付ファイル追加とカスタムメール設定

#### Step 4: 添付ファイル処理を理解する

添付ファイルの抽出と追加処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | service_desk.rb | `app/mailers/emails/service_desk.rb` | setup_service_desk_attachmentsメソッド（251-266行目） |
| 4-2 | service_desk.rb | `app/mailers/emails/service_desk.rb` | add_uploads_as_attachmentsメソッド（268-273行目） |

**主要処理フロー**:
- **256行目**: find_uploaders_for(note)でアップロード抽出
- **258行目**: サイズ制限チェック（10MB）
- **260-262行目**: アップロード内容の読み込み

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

メール本文の構造を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | service_desk_new_note_email.html.haml | `app/views/notify/service_desk_new_note_email.html.haml` | HTML形式のテンプレート |
| 5-2 | service_desk_new_note_email.text.erb | `app/views/notify/service_desk_new_note_email.text.erb` | テキスト形式のテンプレート |

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

```
NotificationService#send_service_desk_notification (app/services/notification_service.rb:900)
    │
    ├─ note.note_metadata&.email_participant（外部参加者判定）
    │
    └─ recipients.each
           │
           ├─ 外部参加者自身チェック（スキップ条件）
           │
           └─ mailer.service_desk_new_note_email(work_item.id, note.id, recipient)
                  │
                  ├─ Note.find(note_id)
                  │
                  ├─ setup_service_desk_mail(work_item_id, recipient)
                  │
                  ├─ setup_service_desk_attachments
                  │      ├─ FileUploader::MARKDOWN_PATTERN.scan
                  │      └─ UploaderFinder.execute
                  │
                  ├─ sender設定
                  │
                  ├─ service_desk_template_content_options('new_note')
                  │
                  ├─ mail_answer_thread(@work_item, options)
                  │
                  ├─ add_uploads_as_attachments
                  │
                  └─ inject_service_desk_custom_email
```

### データフロー図

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

ノート作成            ───▶ NotificationService         ───▶ service_desk_new_note_email
(note.note)               (send_service_desk_notification)   │
                                                           ▼
@note.note            ───▶ Markdown処理               ───▶ メール本文
                           (uploads_as_attachments)        │
                                                           ▼
ノート内アップロード  ───▶ setup_service_desk_attachments ───▶ @uploads_to_attach
                           (サイズ制限10MB)                 │
                                                           ▼
                           add_uploads_as_attachments  ───▶ メール添付
                                                           │
                                                           ▼
                           inject_service_desk_custom_email
                           (SMTP設定)                       │
                                                           ▼
                                                      外部参加者へメール送信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notification_service.rb | `app/services/notification_service.rb` | ソース | 通知サービス |
| service_desk.rb | `app/mailers/emails/service_desk.rb` | ソース | Service Deskメーラーモジュール |
| notify.rb | `app/mailers/notify.rb` | ソース | メインメーラークラス |
| issue_email_participant.rb | `app/models/issue_email_participant.rb` | ソース | 外部参加者モデル |
| service_desk_new_note_email.html.haml | `app/views/notify/service_desk_new_note_email.html.haml` | テンプレート | HTML形式メール本文 |
| service_desk_new_note_email.text.erb | `app/views/notify/service_desk_new_note_email.text.erb` | テンプレート | テキスト形式メール本文 |
| layouts/service_desk.html.haml | `app/views/layouts/service_desk.html.haml` | レイアウト | Service Desk用メールレイアウト |
