# 通知設計書 70-project_scheduled_for_deletion

## 概要

本ドキュメントは、GitLabにおける「プロジェクトが削除予定としてマークされたことを通知する」メール通知機能の設計書である。

### 本通知の処理概要

本通知は、プロジェクトが削除予定としてマークされた際に、プロジェクトのオーナーに通知メールを送信する機能である。

**業務上の目的・背景**：GitLabの遅延削除（Delayed Deletion）機能により、プロジェクトは即座に削除されず、一定期間後に完全削除される。この通知により、プロジェクトオーナーは削除予定を認識し、誤って削除された場合は期限内に復元（retain）操作を行うことができる。

**通知の送信タイミング**：プロジェクトが削除予定としてマークされた直後に送信される。Projects::MarkForDeletionServiceの処理が成功した際にトリガーされる。

**通知の受信者**：プロジェクトのオーナーに送信される。プロジェクトにオーナーがいない場合は、所属グループのオーナーに送信される。

**通知内容の概要**：プロジェクト名、削除までの日数、完全削除予定日、復元用ダッシュボードへのリンクが含まれる。

**期待されるアクション**：ユーザーは削除予定を確認し、誤りであれば期限内にダッシュボードから復元操作を行う。正当な削除であれば、そのまま期限を待つ。

## 通知種別

メール

## 送信仕様

### 基本情報

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

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

1. NotificationService.project_scheduled_for_deletionメソッドが呼び出される
2. プロジェクトのメール通知が有効か確認（emails_disabled?チェック）
3. owners_without_invitesでプロジェクトオーナーを取得
4. プロジェクトにオーナーがいない場合、グループオーナーにフォールバック
5. 各受信者のメールアドレス（@user.email）に送信

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | Gitlab.config.gitlab.email_from |
| 送信元名称 | Gitlab.config.gitlab.email_display_name |
| 件名 | "[project_name] | Project scheduled for deletion" |
| 形式 | HTML/テキスト（マルチパート） |

### 本文テンプレート

```
Hi [username]!

Your project [project_name] has been marked for deletion and will be removed in [X days].

View your project: [project_url]

If this was a mistake, you can retain the project before [deletion_date]: [retention_url]
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @project | 削除予定のプロジェクト | Project.find(project_id) | Yes |
| @user | 通知対象ユーザー | User.find(recipient_id) | Yes |
| @deletion_due_in_days | 削除までの日数 | Gitlab::CurrentSettings.deletion_adjourned_period.days | Yes |
| @deletion_date | 完全削除予定日（フォーマット済み） | permanent_deletion_date_formatted | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作/API | プロジェクト削除予定マーク | プロジェクトのメール通知が有効 | NotificationService.project_scheduled_for_deletion呼び出し時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `project.emails_disabled?` | プロジェクトのメール通知が無効な場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[プロジェクト削除リクエスト] --> B[Projects::MarkForDeletionService#execute]
    B --> C{前提条件チェック}
    C -->|OK| D[execute_deletion]
    C -->|NG| E[エラー返却]
    D --> F{削除マーク成功?}
    F -->|Yes| G[send_notification]
    F -->|No| H[エラー返却]
    G --> I[NotificationService.project_scheduled_for_deletion]
    I --> J{emails_disabled?}
    J -->|Yes| K[処理終了]
    J -->|No| L[owners_without_invites取得]
    L --> M{オーナーあり?}
    M -->|No| N[グループオーナーにフォールバック]
    M -->|Yes| O[各受信者にproject_scheduled_for_deletion]
    N --> O
    O --> P[deliver_later]
    P --> Q[Sidekiqキューに追加]
    Q --> R[非同期でメール送信]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| users | 送信先ユーザー情報取得 | email |
| projects | プロジェクト情報取得 | 名前、パス |
| members | プロジェクトメンバー取得 | オーナー |
| application_settings | 遅延削除期間取得 | deletion_adjourned_period |

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

#### projects

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | プロジェクト識別 | - |
| name | メール本文・件名 | - |
| full_name | プロジェクト表示名 | - |
| namespace_id | 所属namespace | グループオーナー取得時 |

#### application_settings

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| deletion_adjourned_period | 遅延削除期間（日数） | - |

### 更新テーブル一覧

なし（メール送信のみ。プロジェクトの削除マークはMarkForDeletionServiceで処理）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | SMTPサーバー接続エラー | Sidekiqリトライ |
| 宛先不正 | メールアドレス形式エラー | ログ記録、スキップ |
| ユーザー不在 | recipient_idに該当するユーザーがいない | 例外発生（RecordNotFound） |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし

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

- ユーザー名はsanitize_nameでサニタイズされる
- プロジェクトURLはlink_toヘルパーでエスケープされる
- 復元用ダッシュボードURLはシステム設定から取得される安全なURL
- プロジェクトオーナーのみに送信される（権限のあるユーザー）

## 備考

- 遅延削除期間はGitlab::CurrentSettings.deletion_adjourned_periodで設定される
- テンプレートで使用されるinactive_dashboard_projects_urlはプロジェクト復元ダッシュボードへのリンク
- プロジェクトにオーナーがいない場合、親グループのオーナーにフォールバックする仕組みがある
- プロジェクト削除と同時にプロジェクトはアーカイブされ、パスにサフィックスが追加される

---

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

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

### 推奨読解順序

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

通知で使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Project | `app/models/project.rb` | プロジェクトモデル、full_name、emails_disabled? |
| 1-2 | User | `app/models/user.rb` | ユーザーモデル、email |
| 1-3 | Gitlab::CurrentSettings | `lib/gitlab/current_settings.rb` | deletion_adjourned_period設定 |

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

処理の起点となるサービスを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | MarkForDeletionBaseService | `app/services/namespaces/mark_for_deletion_base_service.rb` | 基底クラス、send_notification（行76-78） |
| 2-2 | MarkForDeletionService | `app/services/projects/mark_for_deletion_service.rb` | notification_method（行11-13） |
| 2-3 | NotificationService | `app/services/notification_service.rb` | project_scheduled_for_deletion（行777-788） |

**主要処理フロー**:
1. **行14-28（基底クラス）**: execute内でsend_notificationを呼び出し
2. **行76-78（基底クラス）**: notification_serviceにpublic_sendでnotification_methodを呼び出し
3. **行777-788**: `project_scheduled_for_deletion`メソッドで通知処理
4. **行778**: `emails_disabled?`でプロジェクトのメール通知が有効か確認
5. **行780**: `owners_without_invites`で受信者を取得
6. **行782-787**: 各受信者に`project_scheduled_for_deletion`を送信

#### Step 3: 受信者決定ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NotificationService | `app/services/notification_service.rb` | owners_without_invites（行916-924） |

**主要処理フロー**:
- **行916-924**: `owners_without_invites`メソッド
- **行917**: プロジェクトメンバーからactive_without_invites_and_requests.ownersを取得
- **行919-921**: 空の場合、グループのオーナーにフォールバック

#### Step 4: メーラー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Emails::Projects | `app/mailers/emails/projects.rb` | メール送信ロジック（行35-45） |
| 4-2 | project_scheduled_for_deletion.html.haml | `app/views/notify/project_scheduled_for_deletion.html.haml` | HTMLテンプレート |
| 4-3 | project_scheduled_for_deletion.text.erb | `app/views/notify/project_scheduled_for_deletion.text.erb` | テキストテンプレート |

**主要処理フロー**:
- **行35-45**: `project_scheduled_for_deletion`メソッドでメール生成
- **行36-39**: @project、@user、@deletion_due_in_days、@deletion_dateを設定
- **行43**: 件名「Project scheduled for deletion」

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

```
Projects::MarkForDeletionService#execute（継承元: MarkForDeletionBaseService）
    │
    ├─ preconditions_checks
    │      ├─ can?(current_user, :remove_project, resource)
    │      ├─ resource.self_deletion_scheduled?
    │      └─ resource.ancestor_scheduled_for_deletion?
    │
    ├─ execute_deletion (行19-33)
    │      ├─ resource.schedule_deletion
    │      └─ Projects::UpdateService（archive, path変更）
    │
    └─ send_notification (基底クラス行76-78)
           │
           └─ NotificationService#project_scheduled_for_deletion (行777)
                  │
                  ├─ project.emails_disabled? (行778)
                  │
                  ├─ owners_without_invites(project) (行780, 916-924)
                  │      ├─ project.members.active_without_invites_and_requests.owners
                  │      └─ [fallback] project.group.members...owners
                  │
                  └─ [各受信者に対して]
                         │
                         └─ Notify#project_scheduled_for_deletion (行35)
                                │
                                ├─ @deletion_due_in_days設定
                                ├─ @deletion_date設定（permanent_deletion_date_formatted）
                                │
                                ├─ email_with_layout
                                │
                                └─ deliver_later
                                       └─ Sidekiq Job登録
```

### データフロー図

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

管理者操作                    コントローラー/API
(プロジェクト削除)            (mark_for_deletion)
    │                              │
    ▼                              ▼
project_id ───────────────▶ Projects::MarkForDeletionService
                                   │
                                   ▼
                           前提条件チェック
                           (権限、既存削除マーク)
                                   │
                                   ▼
                           execute_deletion
                           (schedule_deletion + archive)
                                   │
                                   ▼
                           MarkForDeletionBaseService
                           #send_notification
                                   │
                                   ▼
                           NotificationService
                           .project_scheduled_for_deletion
                                   │
                                   ▼
                           owners_without_invites
                           (オーナー取得)
                                   │
                                   ▼
                           Notify#project_scheduled
                           _for_deletion × 受信者数
                                   │
                                   ▼
                           Sidekiq ───────────────────▶ メール送信
                                                        (オーナーへ)
                                                        + 削除予定日
                                                        + 復元リンク
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| MarkForDeletionBaseService | `app/services/namespaces/mark_for_deletion_base_service.rb` | サービス | 削除マーク基底クラス |
| MarkForDeletionService | `app/services/projects/mark_for_deletion_service.rb` | サービス | プロジェクト削除マークロジック |
| NotificationService | `app/services/notification_service.rb` | サービス | 通知ロジックの中核 |
| Emails::Projects | `app/mailers/emails/projects.rb` | メーラー | プロジェクト関連メール定義 |
| Notify | `app/mailers/notify.rb` | メーラー | メインメーラークラス |
| DeletableHelper | `app/helpers/namespaces/deletable_helper.rb` | ヘルパー | permanent_deletion_date_formatted |
| project_scheduled_for_deletion.html.haml | `app/views/notify/project_scheduled_for_deletion.html.haml` | テンプレート | HTML形式のメールテンプレート |
| project_scheduled_for_deletion.text.erb | `app/views/notify/project_scheduled_for_deletion.text.erb` | テンプレート | テキスト形式のメールテンプレート |
